深入理解JavaScript原型与[[Prototype]]继承机制

作者:闭包旅人

时间:2026-10-25

浏览:534

本文深入解析JavaScript中的原型继承机制,详细区分显示的prototype属性与内部的[[Prototype]]属性。通过内存结构图与代码示例,阐明原型链的构建过程、属性搜索的递归逻辑以及isPrototypeOf方法的判定原理,帮助开发者彻底理解JS对象继承与共享的核心实现。

文章目录

prototype与[[Prototype]]的区别

原型属性与内部原型链



在具备面向对象编程基础的情况下,分析以下代码片段:


具体实现如下:

 
// 定义Animal构造函数
function Animal(name){
this.name = name;
} 
// 定义Animal的原型对象
Animal.prototype = {
id:"Animal",
sleep:function(){
alert("sleep");
} 
} 
var dog = new Animal("旺才");
alert(dog.name);//输出:旺才
alert(dog.id);//输出:Animal
dog.sleep()//输出:sleep

其对应的简化内存分配结构图示如下:


prototype与[[Prototype]]的区别

接下来解析该内存图的构建逻辑:



原型链的构建与内部属性

首要明确[[prototype]]与prototype并非同一概念。



首先探讨prototype,每个函数对象均拥有显式的prototype属性,它标识了由该构造函数创建实例的原型。在本例中,Animal.prototype即为dog的原型,dog从Animal.prototype引用的对象中继承属性和方法。



每个对象均包含一个名为[[Prototype]]的内部属性,指向其对应的原型对象。本例中dog的[[prototype]]指向Animal.prototype。由于Animal.prototype也是对象,它同样拥有[[Prototype]]属性指向其原型,从而形成链表结构,即原型链。需注意的是,不同JS引擎对内部[[Prototype]]的命名和可见性处理各异,通常仅在引擎内部使用。尽管JS代码中无法直接访问内部[[Prototype]](Firefox通过__proto__公开),但可通过isPrototypeOf()方法在整个原型链上进行检测。



注:关于函数对象的详细机制,将在后续文章阐述。



属性访问规则

对象属性访问与搜索机制

当使用obj.propName访问对象属性时,处理流程如下(假设obj的内部[[Prototype]]名为__proto__):


1. 若obj自身存在propName属性,则返回该值,否则执行下一步


2. 若obj.__proto__为null,则返回undefined,否则执行下一步


3. 返回obj.__proto__.propName的值


调用对象方法的搜索过程与属性访问一致,因为方法本质上是对象的一个属性值。


提示: 上述步骤隐含递归机制,步骤3中obj.__proto__作为另一对象,将重复执行1, 2, 3步骤以搜索propName。


prototype与[[Prototype]]的区别

这体现了基于Prototype的继承与共享机制。其中object1的方法fn2源自object2,概念上相当于object2重写了object3的fn2方法。


JavaScript中的所有对象均通过prototype链相互关联,顶层为Object,即所有对象均派生自Object类型。



复杂原型继承示例分析

结合上述理论,观察一个更复杂的示例,它清晰阐释了prototype、[[prototype]]、原型链及属性访问的关键点:


具体实现如下:

 
// 定义Animal构造函数
function Animal(name){
this.name = name;
} 
// 定义Animal的原型对象
Animal.prototype = {
id:"Animal",
sleep:function(){
alert("sleep");
} 
} 
function Human(name,age){
Animal.call(this,name);
this.age = age;
} 
Human.prototype = new Animal();
Human.prototype.id = "Human";
Human.prototype.say = function(){
alert("hello everyone,My name is "+this.name +",I'm "+this.age+" and I'm a "+this.id);
} 
// 执行Human相关调用
var jxl = new Human('笨蛋',25);
alert(jxl.name);//输出:笨蛋
alert(jxl.id);//输出:Human
jxl.say();//输出:hello everyone,My name is 笨蛋,I'm 25 and I'm a Human
alert(Animal.prototype.isPrototypeOf(jxl));//输出:true
alert(Object.prototype.isPrototypeOf(jxl));//输出:true

根据上述代码,你能绘制相应的内存图吗?下面展示结果:


prototype与[[Prototype]]的区别

注:prototype链的根为Object.prototype,其内部[[prototype]]属性值为null。


此处尚有许多内容可探讨,但原理已蕴含于该图中。尝试调整代码顺序,例如将Human.prototype.id = "Human";置于Human.prototype = new Animal();之前,观察运行结果并解释原因,这将带来诸多收获。



我发现,通过内存视角展现程序内部运行细节极为完美!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章