当面试官:说说对原型链的理解时,他希望听到什么
分类:javascript
原型链是实现继承的一种方式,扩展了原型搜索机制,使搜索可以继承向上,搜索原型的原型。
原型搜索机制
在原型搜索机制中,构造函数,实例,和原型对象的关系为,原型对象是构造函数的一个属性prototype,原型对象中有一个属性constructor指回构造函数,构造函数生成的实例中具有指针指向构造函数上的原型对象。
代码实现
// 构造函数
function person(name, color){
this.name = name;
this.color = color;
}
// 向默认的原型对象中添加值
person.prototype.getColor = function(){
return this.color;
}
// 通过构造函数构建实例 实例中含有指向原型对象的指针
var personOne = new person("tom", "red");
console.log("来自构造函数中的属性", personOne.name) // "tom"
console.log("来自原型对象中的属性", personOne.getColor()) // "red"
结构图解
构造函数,原型对象,实例的关系如下。
搜索机制
当搜索一个属性/方法时,先搜索实例本身,如果实例本身没有找到,再从指针找到其指向的原型对象,向上查找的特性与作用域链相似。
也就是说,如果在实例中设置了要查找的属性名,就不会再向上查找。
... ...
personOne.getColor = function(){
return "实例中的属性";
}
console.log(personOne.getColor()); // "实例中的属性"
... ...
这时候如果没找到,并且该原型对象中也有一个指向另一个原型对象的指针,就从指针向上搜索原型的原型,直到原型链的末端,也就是原型不再包含指向另一个原型的指针为止。
原型链代码实现
从这里看出,原型链是由指向原型对象的指针将多个原型对象连接起来,上面指出,当我们new一个A实例,实例中就包含一个指向原型对象的指针。
如果我们将该实例赋值为另外一个对象实例B的原型对象,原型链就构成了,查找顺序为。
B实例−>B原型(A实例)−>A原型B实例->B原型(A实例)->A原型
代码实现
在下面的代码中,访问objB的实例对象obj时,通过原型链,最终输出了objA的原型对象中的值。
function objA(){
this.title = 'A';
}
objA.prototype.name = "objA-prototype";
function objB(){
this.title = 'B';
}
objB.prototype = new objA();
var obj = new objB;
console.log(obj.name); // objA-prototype
结构图解
链条拓展
我们可以通过将B实例替换C对象实例的原型对象的方式来拓展该原型链。拓展后查找顺序为。
C实例−>C原型(B实例)−>B原型(A实例)−>A原型C实例->C原型(B实例)->B原型(A实例)->A原型
原型链就是这样实现继承的。
在上面公式中,在C实例向A原型搜索的过程中,只要在某一个地方获得了一个同名属性,就会取该属性,而不再向上搜索,也就是说,在下层原型中添加上层原型链已有的方法会覆盖方法。
原型链的缺点
原型链也有一些缺点,如原型链中的属性是共享的,当修改某一个原型对象中的值时,下层所以实例在获取该值时都会变化,因为大家都是通过指针读取原型对象中的值。
由于这种缺陷,一般不单独使用原型链继承。