JavaScript通过原型继承
当我们利用这个构造函数 new 一个对象的时候
// 构造函数
function Obj (arg) {
this.arg = arg
}
var obj = new Obj()
可分为以下三步
var obj = new Object() //第一步创建obj对象 等价与 var obj = {}
obj.__proto__ = Obj.prototype //让obj的内部属性__proto__指向构造函数的prototype
Obj.call(obj) // 将obj作为this去调用构造函数Obj,从而设置成员(即对象属性和对象方法)并初始化
以上就是一个创建一个对象的过程,接下来让我们来看看怎么通过原型来继承
首先,先看一个简单的继承实现
function A(x) {
this.x = x
}
function B(x, y) {
this.ta = A
this.ta(x)
delete this.ta
this.y = y
}
var b = new B(1, 2)
代码中在构造函数B的内部,临时设置一个引用构造函数A的属性,当我们var b = new B()
的时候在构造函数中this.ta(x)
的this 就是b 这个对象,所以 this.x = x
相当于 p.x = x
,这样构造函数A的x就被继承下来了,但是通过这种方法不能继承原型prototype
中的属性
接下来让我们看看怎么继承原型上的属性
function A(x) {
this.x = x
}
A.prototype.a = "i'm from a prototype"
function B(x, y) {
this.y = y
A.call(this, x)
}
B.prototype.b = "i'm from b prototype"
B.prototype = new A()
B.prototype.b2 = "b2 i'm from b prototype"
B.prototype.constructor = B
var b = new B(1, 2)
这样就完成了一个原型继承
首先我们看看,在A构造函数的原型prototype
上面增加一个a的属性,这样只要通过A构造函数构造出来的对象都可以使用a这个属性,因为当通过A构造出来的对象在实例属性中找不到a这个属性的时候会顺着原型链__proto__
找到构造器原型中的a属性,那么我们要继承的话肯定要把a这个属性一起继承下来
在构造函数B中有一行A.call(this, x)
这一行的作用跟上面写的简单继承中的代码达到一样的效果原理也是让通过B构造出来的对象拥有x这个属性,但是目前为止B还没有继承A原型上的属性
接下来的代码就是本次的重点
B.prototype = new A()
我们让B的原型引用A构造出来的一个对象,这时候也许会有人感到疑惑,有些人甚至想问 为什么不B.prototype = A
呢,typeof Function.prototype
是唯一一个prototype为function类型的,所以基于这点其他构造函数就不能为一个函数了,还有就是我们的原型链是基于__proto__
来实现的,当我们的实例属性找不到对应属性的时候会顺着原型链找,会找__proto__
所指向的构造函数的原型链,但是如果这时候原型链是个函数,所以里面也没有对应的属性这就达不到继承的目的。
B.prototype
的值为{x, __proto__}
其中x
的值为underfind
而__proto__
他是指向A构造函数的prototype
当我们调用b中a的属性时b.a
B构造函数中没有a,他会顺着原型链查找b.__proto__
而 b.__proto__ === B.prototype
通过上面的继承后B.prototype._proto__ === A.prototype
也就是 b.__proto__.__proto__ === A.prototype
一条链下去就找到了在A构造函数中的a属性了。
到这边很多人以为继承已经结束了,其实还有个构造函数,因为B.prototype
是A
的一个实例又因为 A
实例出来的对象的构造函数a.constructor === A.prototype.constructor === A
所以B.prototype.constructor === a.construetor === A
以B构造出来的对象的构造函数自然的都是A了,所以我们要让B原型的constructor
指向B这样就完成了一个原型继承。