JavaScript寄生继承的理解

看了冴羽大大的avaScript深刻之继承的多种方式和优缺点,记录一些理解python

继承究竟是要干什么

js里面继承总给人怪怪的感受,大概是由于js类的概念太奇怪了,和其余语言继承的观感上总有些不一样git

其余语言里,类继承是根本目的方法和属性的复用,那么js里也这么理解。github

须要注意的是,原型链和继承其实不同,尽管属性访问会往回查找原型链,可是继承须要对象的属性时这个实例独有的,冴羽大大提到说是委托,我以为叫代理也okbash

js的类是个伪概念,首先要肯定的是,js类的本质就是一个函数,这个函数会构造一个Object。函数

那么咱们要复用什么呢?首先是属性,子类的对象须要有父类的全部属性。那么就能够调用一下父类的构造函数的属性都初始化到子对象里面。其次是方法。若是是挂在this下面的,调用构造函数的时候就带上了,关键是要是在prototype里面的。既然属性访问会查找原型链,那就把父类的原型添加到子类的原型链中就好了。ui

我这里说得不够清楚,原型上挂在的包括属性和方法this

从上面说的内容继承咱们主要要干几件事:spa

  • 把父类的属性和方法想办法绑定到子类上
  • 把父类添加到子类的原型链上

从头看继承方法

原型链继承

直接把冴羽大大代码拿过来prototype

function Parent () {
    this.name = 'kevin';
}

Parent.prototype.getName = function () {
    console.log(this.name);
}

function Child () {

}

Child.prototype = new Parent();

var child1 = new Child();

console.log(child1.getName())
复制代码

由于Child.prototype是一个Parent对象,因此属性访问就会遍历到父对象,对父类原型方法的访问会遍历到Child.prototype.__proto__时找到,这就是Parent.prototype代理

可是这个方法中,属性其实并非子对象本身持有的,严格来讲不算继承,仍是属于上面说的代理。

还有就是由于变量引用的关系,非基础类型全部子类都是同样的。根本缘由就是上面说的,属性并不禁对象本身持有。

构造函数继承

function Parent () {
    this.names = ['kevin', 'daisy'];
}

function Child () {
    Parent.call(this);
}

var child1 = new Child();

child1.names.push('yayu');

console.log(child1.names); // ["kevin", "daisy", "yayu"]

var child2 = new Child();

console.log(child2.names); // ["kevin", "daisy"]
复制代码

这个办法的核心是用call函数让把子类的this拿给构造函数使用,让全部属性都直接挂在子类的对象中。这和python继承的方式是同样的,很好理解。

那么prototype中的方法可以被访问到吗?

Parent.prototype.t = function () {}
child1.t // undefined
复制代码

是不能的。原型链没有处理过,天然访问不到。因此这个方法的缺陷是很明显的。

组合继承

function Parent (name) {
    this.name = name;
    this.colors = ['red', 'blue', 'green'];
}

Parent.prototype.getName = function () {
    console.log(this.name)
}

function Child (name, age) {

    Parent.call(this, name);
    
    this.age = age;

}

Child.prototype = new Parent();
Child.prototype.constructor = Child;
复制代码

从上面两个能够发现,原型链继承作不到属性独立,经典继承作不到原型链处理,那融合起来就行。

用原型链继承的思路,把父类实例做为子类prototype

再用经典继承的思路,用call调用一次父类构造函数把属性建立在子对象上。

这个方法的问题是,父类的构造函数执行了两次,处理原型链一次,绑定属性一次。太浪费了,这就是寄生继承出现的缘由。

寄生继承

咱们再强调一遍继承要作的事:

  • 把父类的属性和方法想办法绑定到子类上
  • 把父类添加到子类的原型链上

实际上组合继承在每一件事上用了一次构造函数。那么咱们可不可少一次呢?

绑定子类确定是须要把子类的this给父类构造一遍的。这砍不掉,那就考虑原型链。

其实看原型链继承的方法,巧妙地利用了原型链的遍历规则,但其实并非很雅观。直接把一个对象附加到原型上总有地方怪怪的,有种投机取巧的感受。

那么有没有其余方式呢?就是经过中间量。因此就叫寄生了。

思路是这样的:

咱们指望获得的结果是, 能让子类的原型间接和父类联系起来。

若是能有一个原型, prototype是父类的, 而后让子类的prototype指向这个原型,不就联系上了吗?

Obj.prototype = Parent.prototype
Child.prototype = new Obj()
复制代码

能够发现,prototype被添加到了原型链中,并且并无调用构造函数

其实能够看到寄生继承的核心就是,构造一个instance,让instance的prototype代理父类的prototype。

看一看代码

function Parent (name) {
    this.name = name;
    this.colors = ['red', 'blue', 'green'];
}

Parent.prototype.getName = function () {
    console.log(this.name)
}

function Child (name, age) {
    Parent.call(this, name);
    this.age = age;
}

// 关键的三步
var F = function () {};

F.prototype = Parent.prototype;

Child.prototype = new F();
复制代码

有个问题:必定须要中间变量吗,直接把Chile的prototype指向Parent不能够吗?

Child.prototype = Parent.prototype
复制代码

其实这样作是能有效果的,可是两个prototype指向了同一个对象的引用,子类的独立性就没了。这就是为何处理原型链时必须new一次。

相关文章
相关标签/搜索