MST
星途 面试题库

面试题:JavaScript原型链与继承机制的底层深入剖析

从JavaScript引擎底层的角度,解释原型链在内存中的存储结构以及继承机制是如何基于原型链实现的。当访问一个对象的属性时,引擎是如何沿着原型链进行查找的?如果在查找过程中对原型链进行动态修改,会对属性查找产生什么影响?
22.9万 热度难度
编程语言JavaScript

知识考点

AI 面试

面试题答案

一键面试

原型链在内存中的存储结构

  1. 对象与原型的关系:在JavaScript中,每个对象(除了 null)都有一个 [[Prototype]] 内部属性(在现代JavaScript中可以通过 __proto__ 访问,虽然 __proto__ 是非标准的,但被广泛支持),这个属性指向该对象的原型对象。原型对象本身也是一个对象,它也有自己的 [[Prototype]],以此类推形成一条链,直到原型链的末端为 null
  2. 内存表示:从内存角度看,每个对象在内存中除了存储自身的属性和方法外,还会有一个指针指向它的原型对象。这种链式结构形成了原型链。例如,当创建一个函数 function Person() {}Person.prototype 是一个对象,Person 实例(如 new Person())的 [[Prototype]] 指向 Person.prototype

基于原型链的继承机制

  1. 继承的本质:JavaScript通过原型链实现继承。当一个对象需要访问某个属性或方法时,如果自身没有定义该属性或方法,就会沿着原型链向上查找。例如,定义一个 Animal 构造函数和一个 Dog 构造函数,Dog 构造函数通过 Dog.prototype = new Animal() 来设置 Dog 的原型为 Animal 的实例,这样 Dog 的实例就可以继承 Animal 的属性和方法。
  2. 具体实现:当创建 Dog 的实例 let myDog = new Dog() 时,myDog[[Prototype]] 指向 Dog.prototype,而 Dog.prototype[[Prototype]] 又指向 Animal.prototype。当 myDog 访问一个自身没有的属性(如 speak 方法定义在 Animal.prototype 上),JavaScript引擎会沿着 myDog.__proto__ 找到 Dog.prototype,如果没有找到,继续沿着 Dog.prototype.__proto__ 找到 Animal.prototype,直到找到该属性或到达原型链的末端(null)。

属性查找过程

  1. 查找步骤:当访问一个对象的属性时,JavaScript引擎首先在对象自身的属性中查找。如果找到,直接返回该属性的值。如果没有找到,引擎会沿着对象的 [[Prototype]] 指向的原型对象继续查找。这个过程递归进行,直到找到属性或者到达原型链的末端(null)。例如,对于 myDog.speak,先在 myDog 自身属性找,没找到就去 myDog.__proto__(即 Dog.prototype)找,还没找到就去 Dog.prototype.__proto__(即 Animal.prototype)找。

原型链动态修改对属性查找的影响

  1. 添加属性:如果在查找过程中动态地在原型链上添加属性,对于后续的查找会产生影响。例如,在 Animal.prototype 上添加一个新属性 newProperty,那么所有继承自 Animal 的对象(如 Dog 的实例)在后续查找 newProperty 时就能找到。因为下次查找该属性时,沿着原型链会在新添加的位置找到。
  2. 删除属性:如果在原型链上删除一个属性,对于已经缓存了该属性值的对象不会立即生效。但对于新的属性查找,由于该属性已经从原型链上移除,所以不会再找到。例如,删除 Animal.prototype.speak,新创建的 Dog 实例在查找 speak 方法时将找不到,而之前已经调用过 speak 方法并缓存了结果的 Dog 实例,可能仍然可以访问到之前缓存的方法(取决于具体的引擎实现和缓存机制)。