面试题答案
一键面试原型链上属性和方法的查找规则
- 基本规则:当访问一个对象的属性或方法时,JavaScript 首先会在对象自身的属性中查找。如果没有找到,就会沿着原型链向上查找,直到找到该属性或方法,或者到达原型链的顶端(
Object.prototype
)。如果在Object.prototype
上也没有找到,就会返回undefined
。 - 举例说明:假设有构造函数
Parent
和Child
,Child
通过构造函数模式继承Parent
。
function Parent() {
this.parentProp = 'parent value';
}
Parent.prototype.getParentProp = function() {
return this.parentProp;
};
function Child() {
Parent.call(this);
this.childProp = 'child value';
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
let childInstance = new Child();
// 查找 childProp,先在 childInstance 自身查找,找到返回 'child value'
console.log(childInstance.childProp);
// 查找 getParentProp,childInstance 自身没有,沿着原型链到 Child.prototype 查找,没有
// 继续沿着原型链到 Parent.prototype 查找,找到并返回函数,执行函数返回 'parent value'
console.log(childInstance.getParentProp());
多次修改原型对象对继承结构和实例对象的影响
- 对继承结构的影响:多次修改原型对象会改变原型链的指向和内容。如果在继承关系建立后修改原型对象,新添加的属性和方法会影响到所有基于该原型创建的实例对象。同时,原型链的层级关系可能会变得混乱,导致难以理解和维护。
- 对实例对象的影响:对于已经创建的实例对象,它们的原型链已经确定。但是,如果修改原型对象,新添加的属性和方法在实例对象访问时,遵循原型链查找规则,仍然可以访问到。不过,如果修改了原型对象中实例对象已经继承的属性或方法,可能会导致行为的不一致。
- 代码示例:
function Animal() {
this.species = 'animal';
}
Animal.prototype.getSpecies = function() {
return this.species;
};
function Dog() {
Animal.call(this);
this.breed = 'unknown';
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
// 创建一个 Dog 实例
let myDog = new Dog();
// 第一次修改原型对象
Dog.prototype.bark = function() {
return 'Woof!';
};
// 新的实例可以访问 bark 方法
let newDog = new Dog();
console.log(newDog.bark());
// 已经存在的实例也可以访问 bark 方法
console.log(myDog.bark());
// 第二次修改原型对象
Dog.prototype = {
constructor: Dog,
run: function() {
return 'Running...';
}
};
// 新的实例可以访问 run 方法
let anotherDog = new Dog();
console.log(anotherDog.run());
// 但是,已经存在的实例(myDog 和 newDog)无法访问 run 方法,因为它们的原型链还是旧的
// 尝试访问会返回 undefined
console.log(myDog.run());
console.log(newDog.run());
在这个示例中,第一次修改原型对象添加 bark
方法,所有实例都能访问。第二次修改原型对象,重新定义了 Dog.prototype
,新实例可以访问新方法 run
,但旧实例由于原型链已经固定,无法访问新方法。