关于javascript:es6的继承原理

30次阅读

共计 2724 个字符,预计需要花费 7 分钟才能阅读完成。

javascript 有八种类型的数据,其中没有 function,是因为functionobject这个大类蕴含了。也就是说,所有函数都是 object。

当咱们本人申明了一个类,咱们拿到的援用是一个函数的援用,它默认继承于内置的 Object 对象,然而因为 javascript 最顶层的父级(ObjectFunction)设计有点乱,并且不具备普适性,在这里咱们用两个一般的类来演示继承的实现。

将子类申明的援用原型的 [[Prototype]] 指向父类申明的援用原型,这是继承实现的一部分。另一部分是子类申明的援用的 [[Prototype]] 指向父类申明的援用,残缺代码如下:

class A {
}

class B {
}

// B 的实例继承 A 的实例
// 第一局部: 将 B.prototype 的 [[Prototype]] 指向 A.prototype
Object.setPrototypeOf(B.prototype, A.prototype);

// B 继承 A 的动态属性
// 第二局部: 将 B 的 [[Prototype]] 指向 A
Object.setPrototypeOf(B, A);

const b = new B();

以下探讨漠视顶层父类 ObjectFunction,仅探讨一般的存在继承的类之间的关系。

方才说了,继承只须要实现两局部,如下图:

如果要晓得为什么当初这个办法要执行两步,增加两个 [[Prototype]] 援用,那就要晓得构造函数是怎么提供咱们所看见的”类“的。

首先,在构造函数申明的时候,引擎就创立了两个对象:一个是构造函数自身的 function 对象,一个是构造函数的原型对象——一个类型为 object 的对象,它被构造函数的 prototype 属性所援用。构造函数上的属性能够艰深地被了解为动态属性,能够不生成实例就间接调用(这么想,构造函数自身也是一个 function 对象,它在申明的时候就存在了,它的属性就能够应用,和咱们 new 进去能力应用属性的实例实质上是一样的);prototype对象的属性只能被实例调用,且被所有实例共享。

而后,在调用 new 的时候,新创建的对象在执行构造函数之前,就曾经将它的 [[Prototype]] 属性设置给了构造函数的原型对象。借用上图,成果应该是这样的:

所以这就体现出了咱们左边那局部:Son.prototype.[[Prototype]] = Father.prototype的重要性了。依据 js 原型链规定,在寻找一个属性的时候,如果在以后对象上没找到,则会顺着它的原型链,也就是 [[Prototype]] 属性,一个一个往上面的对象找。所以在我给的例子中,查找程序如下:

其中从 Son.prototypeFather.prototype那条线,是咱们通过实现继承连起来的。所以做了这一步之后,Son这个类的实例中找不到的属性就能够到Father.prototype,乃至这条链上更远的原型对象上寻找属性。

原本继承的实现应该到这里就完结了,类实例和其它语言一样,曾经能够拜访所有父类的属性了,然而还不够欠缺:类的动态属性和办法也须要继承。在 es5 中,动态属性和办法间接写在构造函数这个 function 对象的属性中;到了 es6 则是在类申明里应用 static 关键字标识。

所以咱们给(实践上具备父子关系的)构造函数对象增加 [[Prototype]],将它们组织成原型链,这样在寻找Son 的静态方法时,会依照如下程序找:

这就是残缺的,es6 的类继承解决方案。


实现:我这里贴两个实现,一个是 es5 的寄生组合式继承,一个是 es6 的 extends 继承及原理。

es5 诞生了很多继承形式,次要有原型链继承、借用构造函数、组合式继承、寄生式继承和寄生组合式继承。其中寄生组合式继承是最优,代码如下:(起源:https://segmentfault.com/a/11…)

// 父类
function SuperType(name){
    // 父类实例属性
    this.name = name;
    this.colors = ["red", "blue", "green"];
}
// 父类原型办法
SuperType.prototype.sayName = function(){alert(this.name);
};
// 子类
function SubType(name, age){SuperType.call(this, name);//1. 借用构造函数:继承父类的实例属性;this.age = age;
}
//2. 寄生式继承:将父类原型的正本强制赋值给子类原型,实现继承父类的原型办法。inheritPrototype(SubType, SuperType);

SubType.prototype.sayAge = function(){alert(this.age);
};

function inheritPrototype(subType, superType){var prototype = object(superType.prototype); // 创立父类原型的正本
    prototype.constructor = subType; // 将该正本的 constructor 属性指向子类
    subType.prototype = prototype; // 将子类的原型属性指向正本
}

因为 es5 条件无限,不反对间接操作[[Prototype]],所以只能通过创立原型对象的正本这样顺当的形式实现。而且最终也没实现动态属性的继承。

上面是 es6 的代码:(起源:Class 的继承 – ECMAScript 6 入门 (ruanyifeng.com))

class Parent {static myMethod(msg) {console.log('static', msg);
  }

  myMethod(msg) {console.log('instance', msg);
  }
}

class Child extends Parent {static myMethod(msg) {super.myMethod(msg); // super 会在后文讲
  }

  myMethod(msg) {super.myMethod(msg);
  }
}

Child.myMethod(1); // static 1

var child = new Child();
child.myMethod(2); // instance 2

es6 的代码就很扼要。


最初提一嘴 super 关键字吧。

在 es6 中,如果子类有 constructor 的话,则必须调用 super 的构造方法,调用完之后能力应用this

super关键字保留了它在申明时的类援用,并在理论调用时指向它以后的父类。

super关键字我目前理解的还是这些,过后可能会看 ECMA 标准之类的比拟权威有深度的文档弄清楚。

正文完
 0