MST

星途 面试题库

面试题:JavaScript函数继承中的原型链特性深入探究

在JavaScript中,使用构造函数模式实现继承时,原型链上的属性和方法的查找规则是怎样的?如果在继承过程中对原型对象进行多次修改,会对继承结构和实例对象产生什么影响?请详细分析并给出代码示例。
19.1万 热度难度
编程语言JavaScript

知识考点

AI 面试

面试题答案

一键面试

原型链上属性和方法的查找规则

  1. 基本规则:当访问一个对象的属性或方法时,JavaScript 首先会在对象自身的属性中查找。如果没有找到,就会沿着原型链向上查找,直到找到该属性或方法,或者到达原型链的顶端(Object.prototype)。如果在 Object.prototype 上也没有找到,就会返回 undefined
  2. 举例说明:假设有构造函数 ParentChildChild 通过构造函数模式继承 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()); 

多次修改原型对象对继承结构和实例对象的影响

  1. 对继承结构的影响:多次修改原型对象会改变原型链的指向和内容。如果在继承关系建立后修改原型对象,新添加的属性和方法会影响到所有基于该原型创建的实例对象。同时,原型链的层级关系可能会变得混乱,导致难以理解和维护。
  2. 对实例对象的影响:对于已经创建的实例对象,它们的原型链已经确定。但是,如果修改原型对象,新添加的属性和方法在实例对象访问时,遵循原型链查找规则,仍然可以访问到。不过,如果修改了原型对象中实例对象已经继承的属性或方法,可能会导致行为的不一致。
  3. 代码示例
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,但旧实例由于原型链已经固定,无法访问新方法。