Mark24
JavaScript之重新谈谈继承
JavaScript中继承的方法
今天简单的总结下JavaScript中的继承,很多人的博客来自于红宝书、抑或是其他资料。把JavaScript的继承总结出了很多方法,比如:
- 原型法
- 类抄写法
- 组合法
- 组合寄生法
我一直觉得很莫名其妙,然后各种技术博客开始对 1~4的各种方法谈论他的利弊。
实际上呢,我认为 1~3这种就不算是完整的解决方案,不明白他们为什么要把简单的事情讲的那么复杂。
原型链
讲继承就先要讲JavaScript中继承使用的底层原理,就是原型链。原型链的设计可以用4句话概括:
- 构造函数,可以 new 一个对象的实例。构造函数相当于类。实例就是类生成的具体实例对象。
- 每一个构造函数拥有,
.prototype
属性(显式属性),即原型对象,用来保留方法或属性 - 每一个实例对象,拥有
__proto__
属性(隐式属性),指向构造函数的原型对象。 - 当实例对象无法在自身寻找到方法,即去
__proto__
上寻找,找不到就再去 对象的__proto__
上寻找,即原型链寻找
继承
当子类希望构建的实例,找不到属性方法,可以顺着原型链,从父类的原型上寻找方法,以此类推。如此就实现了继承。 一切方法都是围绕着这个来的。
Child 继承 Parent,如下面代码所示,即完成了符合要求的继承:
function Parent() {
// ....
}
function Child (...args) {
Parent.call(this, ...args)
// ....
}
Child.prototype = Object.new(Parent.prototype);
Child.prototype.constructor = Child;
解释:
- 获得父类的属性
Child的构造函数中,运营了Parent的构造函数,改变了this,传参初始化。 这样Child得到了Parent的属性。
- 设置原型和构造器
Object.create(A)
的作用是,创建以A作为 原型的一个对象实例。
Child.prototype = Object.new(Parent.prototype);
的作用是创建了以 Parent.prototype
为原型的一个实例对象,并且作为 Child的原型。这样相当于 建立了 Child.prototype 对象--- Parent.prototype
的关系。
按照原型的要求,正确的给原型对象设置构造器属性,以让原型对象可以找到构造函数。
Child.prototype.constructor = Child;
这样就完成了。
当 const child1 = new Child()
时,child1 实例的 __proto__
指向 Child.prototype
。
当child1找不到方法,就会查找 Child.prototype
, 而 Child.prototype
也找不到就会找他的 __proto__
也就是 Parent.prototype
。
这样就符合要求的串联起来。
至于其他的各种方法,首先残缺的方法,根本不够完整,不应该作为方法记忆。
其次,更高级的变种方法,就是将这种基本方法进行工厂式或者其他形式封装,本质上不变。