js原型和继承
函数和原型和实例的关系
原型prototype
每创建一个函数,函数就会有一个默认属性prototype指向原型(属性集合),而在它的原型上也有一个默认属性constructor指向它自己
实例的__proto__
function Foo(){
this.name = 'dong';
this.say = ()=>{
console.log(this.name)
}
Foo.prototype.xx = 'xx'
}
var obj = new Foo()
console.log(obj.name) // dong
console.log(obj.xx) // xx
看上面的一段代码,我们发现,obj是Foo函数的实例,继承Foo方法的同时还继承了Foo.prototype中的属性。
这是因为每个实例对象都有一个隐藏的属性 proto,这个属性指向创建这个函数的prototype
console.log(obj.__proto__ === Foo.prototype) // true
如此一来我们就形成一个关系图。(图片来源于王福明博客,以下图都是)
自定义函数的prototype本质上就是和 var obj = {} 是一样的,都是被Object创建,所以它的__proto__指向的就是Object.prototype。
但是,Object.prototype确实一个特例——它的__proto__指向的是null
当然,函数也是一个对象,也有__proto__属性,函数的__proto__指定Function.prototype
但是,为什么有Function.__proto__
指向Function.prototype
呢?
其实原因很简单:Function也是一个函数
,函数是一种对象,也有__proto__属性。既然是函数,那么它一定是被Function创建
。所以Function是被自身创建的。所以它的__proto__指向了自身的Prototype
还要注意一点,因为Function.prototype是一个对象,它也有__proto__属性,它的__proto__指向Object.prototype.
最后我们把所有片段整合成一个图。
继承
上面我了解了实例和原型的关系,现在我们通过代码了解下继承。
我们在原型对象上可以看到一个方法hasOwnProperty
,这个方法可以判断该属性是否继承原型。
function Foo(){
this.name = 'dong';
this.say = ()=>{
console.log(this.name)
}
Foo.prototype.xx = 'xx'
}
var obj = new Foo()
obj.a = 'a';
for(var key in obj){
console.log(key)
}
console.log('-----------------分界线------------------------')
for(var item in obj){
if(obj.hasOwnProperty(item)){
console.log(item)
}
}
如图可见,实例obj原有的方法只有Foo和自己后来添加的方法,obj.xx
是继承Foo.prototype
的, 而且 hasOwnProperty
也是从Object.prototype
中来的.
由于所有对象的原型链都会找到Object.prototype,因此所有对象都会有Object.prototype的方法。这就是所谓的“继承”