看了冴羽大大的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一次。