深入理解JavaScript原型与[[Prototype]]继承机制
本文深入解析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]]的区别](/upload/201010/20101013112444611.png)
接下来解析该内存图的构建逻辑:
原型链的构建与内部属性
首要明确[[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]]的区别](/upload/201010/20101013112534854.jpg)
这体现了基于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]]的区别](/upload/201010/20101013112624882.png)
注:prototype链的根为Object.prototype,其内部[[prototype]]属性值为null。
此处尚有许多内容可探讨,但原理已蕴含于该图中。尝试调整代码顺序,例如将Human.prototype.id = "Human";置于Human.prototype = new Animal();之前,观察运行结果并解释原因,这将带来诸多收获。
我发现,通过内存视角展现程序内部运行细节极为完美!
什么时候该用JS构造器:与工厂模式、字面量的选择对比
2026-11-05 16:18:22
推荐几款轻量级JS照片墙开源库及对比
2026-11-05 15:56:19
JavaScript递归写法有哪些常见陷阱及避免方法
2026-11-05 14:53:38
JS数组浅拷贝与深拷贝怎么选?场景与方法总结
2026-11-05 14:41:02
p5.js 中 setup() 和 draw() 函数的执行机制详解
2026-11-05 12:32:38
