了解一下JavaScript继承的方法
在这里需要注意的是,在JavaScript中并没有类的概念,我们的写法只是为了去模仿类,在JavaScript中类的实现是基于原型链、原型委托实现面向对象的。
1、构造函数使用call、apply
function P1() {
this.name = "p1"
}
P1.prototype.say = function() {
console.log('say')
}
function C1() {
P1.call(this) // 关键
this.age = 1
}
复制代码
这是最简单的一种方式,但是这种方式存在一种明显的缺陷,即只能继承构造函数内的属性,不能继承原型链上的属性和方法。
2、原型链
function P2() {
this.name = 'p2'
this.arr = [1,2,3]
}
P2.prototype.say = function() {
console.log('say')
}
function C2() {
this.age = 2
}
C2.prototype = new P2()
let s1 = new C2()
let s2 = new C2()
复制代码
这是通过原型链的方式实现继承,我们通过将子类(C2
)的prototype
属性挂载到父类(P2
)的实例对象(new P2()
)上,当访问访问子类实例没有的方法的时候,会访问子类的prototype
属性,如果子类的prototype
上也没有该方法,则会访问prototype
的__proto__
属性。形成的继承关系: s1.prototype === new P2()
,new P2().__proto__ === P2.prototype
这种方式的实现较第一种有明显的改进,但是也存在一些问题:由于所有实例共用prototype
属性,当我们通过某个实例修改了原型链上的某个属性值的时候,其他的实例也会受到影响。这是违背面向对象的。
3、组合方式
组合方式是将第一种方式和第二种方式结合起来,
function P3() {
this.name = "p3"
this.arr = [1,2,3]
}
P3.prototype.say = function() {
console.log('say')
}
function C3() {
P3.call(this)
this.age = 3
}
C3.prototype = P3.prototype
复制代码
我们把父类的中的属性通过call写到子类中,然后通过子类实例化的每个实例对象中都会有这个属性值,当改变其中一个实例中的属性的时候,其他的实例对象不会受到影响;然后将子类的prototype属性挂载到父类的prototype属性上,就可以访问父类原型上的方法。但是这种方法也存在一些问题,当我们访问C3.prototype
的constructor属性的时候会发现是P3
,这可能会引起一些误解。这是因为我们直接使C3.prototype = P3.prototype
,当我们访问C3.prototype
的时候其实是访问的是,P3.prototype
。这里我们很容易想到重写C3.prototype
的constructor
属性。但是我们必须引入一个中间的变量来表示C3.prototype
然后将中间变量的__proto__
指向父类。如果不引入中间变量当我们修改的C3.prototype
的constructor
,因为C3.prototype、P3.prototype
指向同一个引用,P3.prototype
的constructor
属性也会被修改。
4、组合方式优化
function P4() {
this.name = "p4"
this.arr = [1,2,3]
}
P4.prototype.say = function() {
console.log('say')
}
function C4() {
P4.call(this)
this.age = 4
}
C4.prototype = Obiect.create(P4.prototype)
C4.prototype.constructor = C4
复制代码
这是组合方式的优化方式,通过C4.prototype = Obiect.create(P4.prototype)
这段代码,将C4.prototype
的__proto__
属性指向P4.prototype
,当我们修改了C4.prototype
上的constructor
属性·的时候,P4.prototype
的constructor
属性并不会受到影响。
这里C4.prototype = Obiect.create(P4.prototype) 相当于下面这段代码:
function F() {}
F.prototype = P4.prototype
C4.prototype = new F()
复制代码
5、圣杯模式
圣杯模式,yahoo 贡献的高端写法:YUI3库有个inherit,现在不用了,了解一下:
let inherit = (function() {
let F = function(){}
return function(Child, Father) {
F.prototype = Father.prototype
Child.prototype = new F()
Child.prototype.constructor = Child
Child.prototype.uber = Father.prototype
}
})()
复制代码
这个方法只能继承父类的原型上的方法,不能继承构造函数内部的方法。