深入解析JavaScript对象创建机制与原型链原理
本文深入解析JavaScript中对象创建的底层机制,重点阐述函数对象的[[Construct]]与[[Call]]方法在实例化过程中的作用。通过代码示例说明prototype与[[Prototype]]的区别,解释原型链如何影响属性查找与继承。同时分析对象属性赋值时的本地属性创建逻辑,以及instanceof运算符依赖原型链的判断原理,帮助开发者理解JS面向对象编程的核心概念。
对象创建示例与原型链变化
简单的代码示例
首先展示一段基础代码来引入话题:
具体代码如下所示:
function HumanCloning(){
}
HumanCloning.prototype ={
name:'笨蛋的座右铭'
}
var clone01 = new HumanCloning();
alert(clone01.name);//'笨蛋的座右铭'
alert(clone01 instanceof HumanCloning);//true
HumanCloning.prototype = {};
alert(clone01.name);//'笨蛋的座右铭'
alert(clone01 instanceof HumanCloning);//false
var clone02 = new HumanCloning();
alert(clone02.name);//undefined
alert(clone02 instanceof HumanCloning);//true 复杂的理论
函数对象与[[Construct]]机制
在JavaScript中,只有函数对象(即函数)具备类的概念,因此创建对象必须使用函数对象。函数对象内部包含[[Construct]]和[[Call]]两个内部方法:[[Construct]]用于构造对象,[[Call]]用于普通函数调用。只有使用new操作符时才会触发[[Construct]]逻辑。注:在本例中HumanCloning是一个自定义函数对象,那么Object、String、Number等本地对象是否也是函数对象?答案是肯定的,因为它们可视为函数的派生类型,在某种意义上可与用户定义的函数等同看待。(这与《理解Javascript_04_数据模型》中“内置数据类型”一节相呼应)
例如,var obj=new Object()是利用内置的Object函数对象创建实例obj;而var obj={}和var obj=[]这种语法由JS引擎触发Object和Array的构造过程;var myObj=new fn()则是使用用户定义的类型创建实例。
注:关于函数对象的具体概念将在后续文章中详细讲解,目前可将“函数对象”简单理解为“函数”的概念即可。
内部的实现
对象创建的内部执行步骤
结合本例,函数对象为HumanCloning,其创建的对象实例为clone01和clone02。接下来分析var clone01 = new HumanCloning()的实现细节(注:函数对象的[[Construct]]方法负责实现对象创建):
1. 创建一个内置对象obj并初始化
2. 如果HumanCloning.prototype是Object类型,则将clone01的内部[[Prototype]]设置为HumanCloning.prototype,否则clone01的[[Prototype]]将其初始化为Object.prototype
3. 将clone01作为this,使用args参数调用HumanCloning的内部[[Call]]方法
3.1 内部[[Call]]方法创建当前执行上下文(注:关于执行上下文将在后续博文中讲解,在《Javascript提速_01_引用变量优化》一文中已有部分讲解)
3.2 调用HumanCloning的函数体
3.3 销毁当前的执行上下文
3.4 返回HumanCloning函数体的返回值,如果HumanCloning的函数体没有返回值则返回undefined
4. 如果[[Call]]的返回值是Object类型,则返回这个值,否则返回obj
注意,如下代码是对步骤1、步骤2和步骤3的解释:
具体代码如下:
var clone01 = {};
//程序外部是无法访问[[prototype]]的,仅用于理解
//clone01.[[prototype]] = HumanCloning.prototype;
HumanCloning.call(clone01); 原型链与属性赋值逻辑
注意步骤2中,prototype指对象显示的prototype属性,而[[Prototype]]则代表对象内部Prototype属性(隐式的)。构成对象Prototype链的是内部隐式的[[Prototype]],而非对象显示的prototype属性。显示的prototype只有在函数对象上才有意义,从上面的创建过程可以看到,函数的prototype被赋给派生对象隐式[[Prototype]]属性,这样根据Prototype规则,派生对象和函数的prototype对象之间才存在属性、方法的继承/共享关系。(即原型继承实现原理,正是《理解Javascript_05_原型继承原理》的内容)
注意步骤3.4中描述,让我们来看一个来自于怿飞的问题:

我想,现在回答这个问题,应该是易如反掌吧。
注:原文地址http://www.planabc.net/2008/02/20/javascript_new_function/\n\n内存分析 \n
\n一张简易的内存图,并引入的函数对象的概念,同样也解释了上面代码(相对来说图不是很严谨,但易于理解)。在此也引出了一个问题,instanceof的实现原理,想必大家也看出了一些苗头,instanceof的判断依赖于原型链,具体实现细节,请参见后续博文。 \n本地属性与继承属性 \n对象通过隐式Prototype链能够实现属性和方法的继承,但prototype也是一个普通对象,就是说它是一个普通的实例化的对象,而不是纯粹抽象的数据结构描述。所以就有了这个本地属性与继承属性的问题。 \n首先看一下设置对象属性时的处理过程。JS定义了一组attribute,用来描述对象的属性property,以表明属性property是否可以在JavaScript代码中设值、被for in枚举等。 \nobj.propName=value的赋值语句处理步骤如下: \n1. 如果propName的attribute设置为不能设值,则返回 \n2. 如果obj.propName不存在,则为obj创建一个属性,名称为propName \n3. 将obj.propName的值设为value \n可以看到,设值过程并不会考虑Prototype链,道理很明显,obj的内部[[Prototype]]是一个实例化的对象,它不仅仅向obj共享属性,还可能向其它对象共享属性,修改它可能影响其它对象。 \n我们来看一个示例:
具体代码如下:
function HumanCloning(){
}
HumanCloning.prototype ={
name:'笨蛋的座右铭'
}
var clone01 = new HumanCloning();
clone01.name = 'jxl';
alert(clone01.name);//jxl
var clone02 = new HumanCloning();
alert(clone02.name);//笨蛋的座右铭 结果很明确,对象的属性无法修改其原型中的同名属性,而只会自身创建一个同名属性并为其赋值。
参考。
https://www.jb51.net/article/25008.htm
JS split切割后出现空字符串怎么过滤
2026-11-05 16:00:30
在 React 或 Vue 项目中封装使用 JS 节流防抖
2026-11-05 15:27:29
JavaScript中怎么将带小数的字符串转为整数?
2026-11-05 14:32:38
怎么用JavaScript策略模式重构电商促销计算逻辑?
2026-11-05 14:01:09
JS使用constructor判断数据类型可靠吗
2026-11-05 13:49:41
