最近在看vue的原始碼,其中有一部分牽扯到了繼承,看到es5中靜態方法的"繼承"是這樣寫的
const Sub = function VueComponent (options) {
this._init(options)
}
Sub.prototype = Object.create(Super.prototype)
Sub.prototype.constructor = Sub
...
Sub.extend = Super.extend
Sub.mixin = Super.mixin
Sub.use = Super.use
複製程式碼
es5的繼承大家都知道(通過原型鏈實現原型屬性的繼承,通過建構函式實現例項屬性的繼承),但程式碼中靜態方法是直接從父類賦給子類的,這就想到的一個問題?靜態方法沒有繼承嗎?帶著這個問題研究了一下靜態方法在es5繼承中和es6繼承中的差異
es5中靜態方法的繼承
// 定義父類
function Super(name, age) {
this.name = name;
this.age = age;
};
Super.prototype.sayName = function() {
return this.name;
};
// 定義屬性
Super.num = 1;
// 定義靜態方法
Super.sayWord = function(word) {
return word;
};
// 定義子類
function Sub(name, age, sex) {
// 繼承例項屬性
Super.call(this, name, age);
this.sex = sex;
}
// 繼承父類的原型方法+原型屬性
Sub.prototype = Object.create(Super.prototype);
Sub.prototype.constructor = Sub;
var instance = new Sub('張三', '18', '男');
複製程式碼
分別列印出來看下結果:
console.log(Super.sayWord('hello world'));
// print TypeError: Sub.sayWord is not a function
console.log(Sub.sayWord('hello world'));
// print TypeError: Sub.sayWord is not a function
console.log(instance.sayWord('hello world')); // 例項是無法獲取到靜態方法的
// print instance.sayWord is not a function
複製程式碼
靜態屬性和靜態方法類似,大家可以自行驗證,通過上面的我們可以得出下面的結論: es5的類繼承:子類是無法繼承父類的靜態屬性和靜態方法的,只是通過傳遞
其中有個需要注意的地方:靜態方法沒辦法獲取到this,由於JavaScript中的類也是一個物件,靜態方法相當於是這個類物件的一個屬性方法,但是this指的是當前例項物件而不是類物件,所以是無法取到的,下面看下es6中的
es6中靜態方法的繼承
class Super {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayName() {
return this.name;
}
static sayWord(word) {
return word;
}
static get num() {
return this._num; // 因為類也是一個物件 所以_num 只是這個類上的屬性 並非例項屬性
}
static set num(n) {
this._num = n;
}
}
class Sub extends Super {
constructor(name, age, sex) {
super(name, age);
this.sex = sex;
}
}
const instance = new Sub('張三', '18', '男');
複製程式碼
下面我們分別看下靜態方法和靜態屬性
console.log(Super.sayWord('hello world'));
// print hello world
console.log(Sub.sayWord('hello world'));
// print hello world
console.log(instance.sayWord('hello world'));
// print TypeError: instance.sayWord is not a function
複製程式碼
可以看出es6繼承中,靜態方法被繼承下來,這點與es5還是有差異的,再看靜態屬性, es6中的靜態屬性可以直接[類名].[屬性名]這樣的定義,也可以如上面那樣定義(get,set),只是get,set這樣的定義我們可以更好進行的控制
Super.num = 3;
console.log(Super.num);
// print 3
console.log(Sub.num);
// print 3
複製程式碼
看的出,靜態屬性也被繼承了,但是這樣也有問題,當靜態屬性是引用型別時,子類和父類指向的同一個地址,父類如果發生變化子類也會跟著變化,這是我們不希望看到的
Super.num = [];
Sub.num.push(1);
console.log(Super.num);
// print [1]
console.log(Sub.num);
// print [1]
Super.num.push(2);
console.log(Super.num);
// print [1,2]
console.log(Sub.num);
// print [1,2]
複製程式碼
我們修改下靜態屬性num的get方法,使其只獲取自己當前類的值,而非繼承的值
static get num() {
return this.hasOwnProperty('_num') ? this._num : undefined;
}
複製程式碼
再測試一下:
Super.num = [];
// Sub.num.push(1);
// print TypeError: Cannot read property 'push' of undefined 因為Sub.num得到的是 undefined
Sub.num=1;
console.log(Super.num);
// print []
console.log(Sub.num);
// print 1
Super.num.push(2);
console.log(Super.num);
// print [2]
console.log(Sub.num);
// print 1
複製程式碼
可以得出:es6中的靜態方法和靜態屬性都可以被繼承下來的,只是靜態屬性的繼承需要稍作處理,否則就被共享了
下面我們再總結下:
- es5和es6實現繼承的方式是不同的,前者通過原型鏈實現對父類原型方法、原型屬性的繼承,通過建構函式的呼叫實現對例項方法,例項屬性的呼叫,後者通過extends關鍵字實現繼承
- es5中靜態方法、靜態屬性是無法通過繼承下來的,只能通過賦值傳遞,如最開始的demo,但es6中則是可以的