原型和原型链
对于很多初学JavaScript的同学刚开始接触原型和原型链的时候有大部分一头雾水,prototype和__proto__到底是什么,原型对象是什么,原型链又是什么,希望看完下面的文章对你的理解会有帮助。
原型
对于构造函数来说,生成实例的时候,该属性会自动成为实例对象的原型。
function Animal(name) {
this.name = name;
}
Animal.prototype.color = 'white';
var cat1 = new Animal('大毛');
var cat2 = new Animal('二毛');
cat1.color // 'white'
cat2.color // 'white'
上面代码中,给Anlmal的prototype属性,就是实例对象的cat1个cat2的原型对象。原型上添加了一个color属性,最后,实例对象都共享了该属性。
原型对象的属性不是实例对象自身的属性。只要修改原型对象,变动就立刻会体现在所有实例对象上。
改变他的原型对象,实例对象就会立刻体现在所有实例对象上。
Animal.prototype.color = 'yellow';
cat1.color // "yellow"
cat2.color // "yellow"
上面代码中,原型对象的color值发生改变,两个实例对象的color属性立刻跟着改变。这是因为实例对象本身没有color属性,都是从原型对象上的color读取过来的。
也就是说,当实例对象本身没有某个属性或方法的时候,他会到原型对象去寻找属性或方法,这就是原型对象的特殊之处。
如果实例对象自身就有某个属性或方法,就不会再去原型对象上去寻找这个属性或方法。
cat1.color = 'black';
cat1.color // 'black'
cat2.color // 'yellow'
Animal.prototype.color // 'yellow';
上面代码中,实例对象cat1的属性修改为black,他本身就有yellow的属性,并不会再去原型对象去读取color属性。
总结一下:原型对象的作用,就是定义所有实例对象共享的属性和方法。也就是它被称为原型对象的原因,而实例对象可以看做是从原型对象衍生出来的子对象。
Animal.prototype.walk = function () {
console.log(this.name + ' is walking');
};
上面代码中,Animal.prototype对象上定义了一个walk方法,这个方法将可以在所有Animal 实例对象上面调用。
原型链
javascript规定,所有对象都有自己的原型对象(prototype)。
一方面,任何一个对象,都可以充当其他对象的原型,另一方面,由于原型对象也是一个对象,他也有自己的原型。因此,就会形成一个原型链 对象到原型,再到原型的原型
如果一层层的上溯,所有对象的原型都最终可以上溯到Object.prototype,说白话就是Object构造函数的prototype属性。
也就是说,所有对象都继承了Object.prototype的属性、这就是所有对象都有valueOf和tostring方法的原因,因为这是从Object.prototype继承来的。
那么有的人就问了,Object.prototype对象有没有他的原型呢?
回答是:Object.prototype的原型是null。null没有任何的属性和方法,也没有自己的原型。因此,原型链的尽头就是null
Object.getPrototypeOf(Object.prototype)
// null
上面的代码表示,Object.prototype对象的原型就是null!!!
由于null没有任何属性,所以原型链就到此为止。
读取对象的某个属性时,javascript引擎先寻找对象本身的属性,如果找不到,就会到它的原型去找,如果他的原型还是找不到,就会到他原型的原型去找,如果到最顶层Object.prototype还是找不到,则会返回undefined
如果对象自身和他的原型,都定义了同名属性、那么会优先读取对象自身的属性,这叫"覆盖"
注意点来了!一层层向上,在整个原型链上去寻找某个属性,这对性能是有影响的。寻找的属性越是在上层的原型对象,对性能的影响就会越大。如果寻找某个不存在的属性,将会遍历整个原型链。
结尾
上述内容来源于阮一峰老师的文档,初次接触原型和原型链的同学可能不太好理解,自己多看看文档多写一写代码,能更好深入的理解。
文中有错误或者缺少的地方欢迎各位指点出来,谢谢!