本文通过对比代码示例,深入解析 JavaScript 原型对象与原型链机制。重点阐述实例化(new)与原型重置的先后顺序如何影响方法继承,指出 prototype 仅有一份而实例共享其引用。通过图解和代码演示,明确 __proto__ 指针在原型链查找中的作用,帮助开发者避免常见的原型覆盖错误。
通过一个具体案例,彻底厘清 JavaScript 中原型对象与原型链的核心概念。
引言与背景
引言部分
此前,我对 JavaScript 的原型链和原型对象仅有模糊的认知。当被问及这些概念时,往往只能给出官方却缺乏深度的解释。正如那句名言所说:若无法用简洁语言阐述复杂事物,便意味着并未真正理解。近期研读《JavaScript 高级程序设计》后,我对这一机制有了全新认识。下文将借助对比示例进行说明。
常规原型方法定义
常规写法示例
function Person () {
this.name = 'John';
}
var person = new Person();
Person.prototype.say = function() {
console.log('Hello,' + this.name);
};
person.say();//Hello,John
上述代码结构简洁:在 Person 的原型对象上定义公共方法 say。尽管该方法在实例构造后声明,但由于原型方法在调用前已存在,后续创建的每个实例均可访问此方法。由此可得出一个关键结论:
原型对象的核心作用是为所有实例存储共享的方法与属性,它本质上只是一个普通对象。所有实例共享同一个原型对象,因此原型对象在内存中仅存在一份。这确保了如下等式成立:
person.say == new Person().say
原型重置导致的错误分析
另一种常见的错误写法
function Person () {
this.name = 'John';
}
var person = new Person();
Person.prototype = {
say: function() {
console.log('Hello,' + this.name);
}
};
person.say();//person.say is not a function
遗憾的是,上述代码会报错,因为无法找到 person.say 方法。这种写法的初衷是好的:若需在原型上添加更多属性或方法,每次重复书写 Person.prototype 显得繁琐,因此试图将其提炼为直接赋值给 Object 的形式。然而,该示例的关键在于实例化操作发生在原型方法添加之前,这导致了一个严重问题:
当执行 var person = new Person() 时,此时 Person.prototype 指向一个空对象 {}(内部包含 constructor 属性)。对于实例 person 而言,其内部存在一个原型链指针 __proto__,该指针指向 Person.prototype 当前指向的对象,即 {}。随后,代码重置了 Person 的原型对象,使其指向另一个包含 say 方法的对象,即:
Object {say: function}
此时,person.__proto__ 的指向并未改变,它依然指向那个空的 {} 对象。由于该空对象内部没有 say 方法,因此调用时抛出错误。
从这一现象可以总结出:
在 JavaScript 中,当对象调用某个方法时,引擎会首先在对象自身内部查找。若未找到,则沿着原型链向上查找,依次层层递进。这里的原型链即指实例对象的 __proto__ 属性。
若要使上述示例正常运行,最简单有效的方法是交换对象构造与原型重置的顺序,即:
function Person () {
this.name = 'John';
}
Person.prototype = {
say: function() {
console.log('Hello,' + this.name);
}
};
var person = new Person();
person.say();//person.say is not a function
原型链结构图解与总结
图解原型链结构

理解原型对象的结构是掌握原型链的关键:
Function.prototype = {
constructor : Function,
__proto__ : parent prototype,
some prototype properties: ...
};
总结而言,函数的原型对象 constructor 默认指向函数本身。除了原型属性外,为实现继承机制,原型对象还包含一个原型链指针 __proto__,该指针指向上一层的原型对象。上一层原型对象的结构与此类似,通过 __proto__ 一直指向 Object 的原型对象。Object 的原型对象通过 Object.__proto__ = null 表示原型链的顶端。这种机制形成了 JavaScript 的原型链继承,同时也解释了为何所有 JavaScript 对象都具备 Object 的基本方法。
以上内容为本文的全部总结,希望能为读者的学习或工作提供帮助,同时也感谢大家的支持。