最近在看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中则是可以的