面试题答案
一键面试原型链在内存中的存储结构
- 对象与原型的关系:在JavaScript中,每个对象(除了
null
)都有一个[[Prototype]]
内部属性(在现代JavaScript中可以通过__proto__
访问,虽然__proto__
是非标准的,但被广泛支持),这个属性指向该对象的原型对象。原型对象本身也是一个对象,它也有自己的[[Prototype]]
,以此类推形成一条链,直到原型链的末端为null
。 - 内存表示:从内存角度看,每个对象在内存中除了存储自身的属性和方法外,还会有一个指针指向它的原型对象。这种链式结构形成了原型链。例如,当创建一个函数
function Person() {}
,Person.prototype
是一个对象,Person
实例(如new Person()
)的[[Prototype]]
指向Person.prototype
。
基于原型链的继承机制
- 继承的本质:JavaScript通过原型链实现继承。当一个对象需要访问某个属性或方法时,如果自身没有定义该属性或方法,就会沿着原型链向上查找。例如,定义一个
Animal
构造函数和一个Dog
构造函数,Dog
构造函数通过Dog.prototype = new Animal()
来设置Dog
的原型为Animal
的实例,这样Dog
的实例就可以继承Animal
的属性和方法。 - 具体实现:当创建
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
)。
属性查找过程
- 查找步骤:当访问一个对象的属性时,JavaScript引擎首先在对象自身的属性中查找。如果找到,直接返回该属性的值。如果没有找到,引擎会沿着对象的
[[Prototype]]
指向的原型对象继续查找。这个过程递归进行,直到找到属性或者到达原型链的末端(null
)。例如,对于myDog.speak
,先在myDog
自身属性找,没找到就去myDog.__proto__
(即Dog.prototype
)找,还没找到就去Dog.prototype.__proto__
(即Animal.prototype
)找。
原型链动态修改对属性查找的影响
- 添加属性:如果在查找过程中动态地在原型链上添加属性,对于后续的查找会产生影响。例如,在
Animal.prototype
上添加一个新属性newProperty
,那么所有继承自Animal
的对象(如Dog
的实例)在后续查找newProperty
时就能找到。因为下次查找该属性时,沿着原型链会在新添加的位置找到。 - 删除属性:如果在原型链上删除一个属性,对于已经缓存了该属性值的对象不会立即生效。但对于新的属性查找,由于该属性已经从原型链上移除,所以不会再找到。例如,删除
Animal.prototype.speak
,新创建的Dog
实例在查找speak
方法时将找不到,而之前已经调用过speak
方法并缓存了结果的Dog
实例,可能仍然可以访问到之前缓存的方法(取决于具体的引擎实现和缓存机制)。