MDN上类的继承方式
上一篇说了js中的实例化,这篇说一下js中的类的继承方式
经常能搜到一些总结js继承方式的文章,原型链继承,构造函数继承,组合继承,寄生继承,组合寄生继承, 新手看完之后肯定是越看越懵逼,这些文章写到最后一定是推荐使用组合寄生继承,因为只有这种方式是最完美的。
这篇咱们不看那么多,只看前端的圣经,MDN上是如何实现类继承的。
MDN上只说了单继承和多继承,很好理解,单继承就是只有一个父类,多继承就是有多个父类
首先要明确的概念,对象的属性和方法在js中其实都可以看作是属性,方法是值为函数的属性。下面说到的属性二字都包括方法。
实例属性是每个实例所独有的,不应该受到其他实例影响,换个说法就是实例属性不能是引用关系。
原型属性则是所有实例所公用的,这也正是类存在的意义:实现部分属性的复用,从而减少代码量,提高性能,并且更加符合人类对于事物的抽象认知,也就是面向对象。
所以原型属性是引用关系。
以下代码粘贴自MDN,部分汉字为我加的注释
// 单继承
// Shape - 父类(superclass)
function Shape() {
this.x = 0;
this.y = 0;
}
// 父类的方法
Shape.prototype.move = function(x, y) {
this.x += x;
this.y += y;
console.info('Shape moved.');
};
// Rectangle - 子类(subclass)
function Rectangle() {
// new Rectangle()时就会把父类代码里的this.x = 0;this.y = 0;引入到这里
// 同时this指向的是子类的上下文,实现实例属性的继承
Shape.call(this); // call super constructor.
}
// 子类续承父类
Rectangle.prototype = Object.create(Shape.prototype);
// Shape.prototype.constructor是 Shape
// 如果不修正的话,Rectangle.prototype.constructor也会变成Shape,显然不符合常理
Rectangle.prototype.constructor = Rectangle;
var rect = new Rectangle();
console.log('Is rect an instance of Rectangle?',
rect instanceof Rectangle); // true
console.log('Is rect an instance of Shape?',
rect instanceof Shape); // true
rect.move(1, 1); // Outputs, 'Shape moved.'
懂得的同学看完以后就会说:这不就是组合寄生式继承么。
Shape.call(this) 这句代表组合
Rectangle.prototype =Object.create(Shape.prototype) 这句代表寄生
组合可以理解为没有引用关系,属性之间都是独立的
寄生可以理解为子类复用父类的属性
rect.move === Shape.prototype.move // true
当然,MDN不会搞这么变态的一个名字来命名它。你只需要知道这种方式就是正统写法,当然其他的方式最好也要理解,但不要被搞懵逼了。
代码分析
许多同学会有疑问,其中这一句
Rectangle.prototype = Object.create(Shape.prototype);
可以写成下面这样么?
Rectangle.prototype = Shape.prototype
你可以这么改写一下,把代码复制到控制台,执行以后返回的结果仍然是符合预期的。
区别就是如果这么写了,这两个原型就会是一种引用关系!
当修改Rectangle.prototype.move = xxx 时,Shape.prototype.move 也会被修改。
如果按正统写法Rectangle.prototype = Object.create(Shape.prototype),
当修改Rectangle.prototype.move = xxx 时,Shape.prototype.move仍会保持原来的值,更加符合我们的常规需求。
也就是子类可以重写父类的同名属性。
于是Object.create的作用就很好理解了,就是把Shape.prototype这个对象在原型链上向上推了一层,并且重新生成一个对象。
多继承
接下来还有类的多继承,如果上面的理解了,多继承就没啥说的了。
如果用组合寄生的概念来解释,那就是多组合,多寄生。
Object.assign把多个父类原型合并一下,赋值给子类的原型。
代码如下
function MyClass() {
SuperClass.call(this); // 多组合
OtherSuperClass.call(this);
}
MyClass.prototype = Object.create(SuperClass.prototype);
// 多寄生
Object.assign(MyClass.prototype, OtherSuperClass.prototype);
MyClass.prototype.constructor = MyClass;
MyClass.prototype.myMethod = function() {
// do a thing
};
es6里extends就不说了,实际上就是这种继承方式的语法糖。
欢迎来我的b站空间逛逛
https://space.bilibili.com/395672451