前面也写过原型链的文章,但是发现从这个方面分析原型链更好理解,所以记录了下来,实例对象都有隐式原型(__proto__
),函数对象都有显式原型(prototype
)记住下面两句话句话再去看图对你理解原型链很有帮助:
- 实例对象的隐式原型指向构造函数的显式原型
- 函数的显示原型指向的对象默认为空Object实例对象(Object除外)
如果看完图还是不明白,还是请你去看前面两句话,这里解释一下:函数的显示原型指向的对象默认为空Object
实例对象(Object
除外),为什么Object
除外呢?如果按照我们上面的话就应该是这样console.dir(Object.prototype.__proto__ === Object.prototype)//true
但是Object.prototype.__proto__=null
所以这个特殊,不知道大家发现了图上的一个问题,那就是函数对象,既然也是对象,也应该有__proto__
才对啊,确实是有的,同样的还是记住一句话:
所以函数都是Function的实例(它自身也不例外)–console.dir(Function.__proto__ === Function.prototype)//true
换句话说就是:函数的__proto__
都是一样的,用代码表示就是console.dir(Fn.__proto__ === Function.prototype)//true
总结起来:
- 函数的显示原型指向的对象默认为空Object实例对象(Object不满足)
- 实例对象的隐式原型指向构造函数的显式原型
- 所以函数都是Function的实例(它自身也不例外)
- Object的原型对象就是原型链的尽头
有了这个图再来做做几个题:
console.log(Object instanceof Function)//true
console.log(Object instanceof Object)//true
console.log(Function instanceof Function)//true
console.log(Function instanceof Object)//true
那我们怎样实现继承的呢,我们这里可以用组合继承来实现,和我之前写的文章有点区别,之前文章是指定原型实现的,这个是组合继承,看图
这就是继承的过程,在左侧的代码中,实例对象xf
继承了父类的Parent
原型中的方法show()
,但是如果是左侧那样写的话,还是有点问题,正如我图中画的那样,我们在Children
的原型还没有更该过来之前,就将方法加入到了原型中去了,所以实例化对象的时候,实例化的__proto__
的指到了新的对象上,正如入所示,指向Parent
的实例对象,所以此时调用play()
会报错的,正确做法是,把给Children
添加原型方法的时机放到改变原型对象之后,也即左侧的Children.prototype.play = function (){console.log(this.age)}
放到Children.prototype = new Parent()
之后即可
欢迎大家指正!