JavaScript中的new与内存模型
需要明确类型标识(如。
目录
new与直接创建的区别
一、new
与直接创建的区别
1. 内存分配机制
-
直接创建(字面量):
const obj = { name: "Alice" };
-
内存模型:
-
对象直接分配在堆内存中。
-
其隐式原型(
__proto__
)自动指向Object.prototype
。
-
-
特点:无构造函数参与,无法通过
instanceof
追溯类型。
-
-
使用
new
:function Person(name) { this.name = name; } const obj = new Person("Bob");
-
内存模型:
-
创建一个空对象,并将其
__proto__
链接到Person.prototype
。 -
构造函数逻辑(
Person
)在该对象的上下文中执行。
-
-
特点:对象类型明确(
obj instanceof Person
为true
),可继承原型方法。
-
二、内存模型详解
1. new
的内存分配过程
当执行 new Person("Bob")
时,内存中发生以下步骤:
-
创建空对象:
const obj = Object.create(Person.prototype); // 原型链绑定
-
堆内存中分配新对象,
obj.__proto__
→Person.prototype
。
-
-
绑定
this
并执行构造函数:Person.call(obj, "Bob"); // 将 this 指向 obj
-
构造函数内部对
this
的操作(如this.name = "Bob"
)会写入obj
的内存空间。
-
-
返回对象:
-
若构造函数未显式返回对象,则返回
obj
; -
若构造函数返回 对象(包括数组、函数等),则替代
obj
。 -
若返回 非对象值(如
number
、string
),则忽略返回值,仍返回obj
。
-
2. 直接创建对象的内存模型
const obj = { name: "Alice" };
-
堆内存结构:
-
对象直接分配在堆中,属性值(如
name
)存储在对象内部。 -
obj.__proto__
→Object.prototype
。
-
三、变量在内存中的位置
1. 栈内存 vs 堆内存
-
栈内存:
-
存储 基本类型值(
number
、string
、boolean
等)和 引用地址。 -
例如:
const age = 30; // 基本类型,值存在栈中 const ref = obj; // 引用地址存在栈中,指向堆中的对象
-
-
堆内存:
-
存储 引用类型值(对象、数组、函数等)。
-
例如:
const obj = { name: "Alice" }; // 对象本身在堆中
-
2. new
创建的对象内存布局
假设有以下构造函数:
function Person(name) {
this.name = name; // 实例属性(存储在对象自身)
}
Person.prototype.greet = function() { // 原型方法(存储在原型)
console.log(this.name);
};
const p = new Person("Bob");
-
内存结构:
p (栈中的引用地址) │ └─→ 堆中的 Person 实例对象 ├─ name: "Bob" // 实例属性 └─ __proto__: → Person.prototype ├─ greet: function() // 原型方法 └─ __proto__: → Object.prototype
四、性能与内存优化
1. 方法共享(原型 vs 实例)
-
实例方法(每个对象独立):
function Person(name) { this.name = name; this.greet = function() { ... }; // 每个实例独占一个方法 }
-
内存开销:方法重复存储,占用更多堆内存。
-
-
原型方法(共享):
Person.prototype.greet = function() { ... }; // 所有实例共享
-
内存优化:方法仅存一份,通过原型链访问。
-
2. 闭包与内存泄漏
若构造函数中使用了闭包:
function Person(name) {
this.getName = function() { return name; }; // 闭包保留 name
}
-
内存影响:每个实例的
getName
方法独立,且闭包中的name
无法被 GC 回收,直到实例销毁。
五、关键区别总结
特性 | new 创建的对象 |
直接创建的对象 |
---|---|---|
原型链 | __proto__ → 构造函数原型 |
__proto__ → Object.prototype |
类型标识 | instanceof 可检测类型 |
无法追溯自定义类型 |
方法存储 | 方法可共享于原型 | 方法需直接定义在对象中 |
内存占用 | 实例属性独立,原型方法共享 | 所有属性和方法独立 |
构造函数逻辑 | 可执行初始化代码 | 无初始化逻辑 |
六、代码示例对比
1. 直接创建对象
const obj1 = { name: "Alice" };
const obj2 = { name: "Bob" };
console.log(obj1.greet === obj2.greet); // false(方法不共享)
2. new
创建对象
function Person(name) { this.name = name; }
Person.prototype.greet = function() { console.log(this.name); };
const p1 = new Person("Alice");
const p2 = new Person("Bob");
console.log(p1.greet === p2.greet); // true(方法共享)
七、总结
-
new
的核心价值:实现面向对象的实例化、原型继承和方法共享。 -
内存效率:通过原型链共享方法,减少内存占用。
-
变量存储:
-
基本类型存在栈中,引用地址指向堆中的对象。
-
实例属性在堆中独立存储,原型方法在原型对象中共享。
-
-
应用场景:
-
需要类型标识和方法共享 → 使用
new
。 -
简单数据结构 → 直接创建对象。
-
new底层机制
一、new
的执行流程(底层四步)
当执行 const obj = new Constructor(arg)
时,底层发生以下步骤:
1. 创建空对象并绑定原型
const obj = Object.create(Constructor.prototype);
-
内存操作:在堆中分配新对象,其隐式原型(
__proto__
)指向构造函数的prototype
。 -
原型链效果:新对象可访问
Constructor.prototype
上的属性和方法。
2. 绑定 this
并执行构造函数
const result = Constructor.apply(obj, args);
-
上下文切换:构造函数内部的
this
被强制绑定到新对象obj
。 -
属性初始化:构造函数中通过
this.xxx = ...
定义的属性会被写入obj
的内存空间。
3. 处理构造函数返回值
if (typeof result === 'object' && result !== null) {
return result; // 构造函数返回对象,替代默认对象
} else {
return obj; // 否则返回新创建的对象
}
-
规则:
-
若构造函数返回 对象(包括数组、函数等),则替代
obj
。 -
若返回 非对象值(如
number
、string
),则忽略返回值,仍返回obj
。
-
4. 完成对象创建
-
最终结果:
obj
(或被替代的对象)成为new
表达式的结果。
二、底层机制模拟(手写 new
函数)
以下代码模拟 new
的底层行为:
function myNew(Constructor, ...args) {
// 1. 创建空对象,并绑定原型链
const obj = Object.create(Constructor.prototype);
// 2. 执行构造函数,绑定 this
const result = Constructor.apply(obj, args);
// 3. 处理返回值
return result instanceof Object ? result : obj;
}
// 使用示例
function Person(name) { this.name = name; }
const p = myNew(Person, "Alice");
console.log(p.name); // "Alice"
三、关键底层特性
1. 构造函数内部的 return
陷阱
-
返回对象:替代默认对象
function Person() { return { name: "Alice" }; } const p = new Person(); // p 是 { name: "Alice" }
-
返回非对象:忽略返回值。
function Person() { return 42; } const p = new Person(); // p 是 Person 实例
2. 性能优化
-
方法共享:将方法定义在原型上(
Person.prototype.method
),而非构造函数内部,避免每个实例重复创建方法。 -
内存泄漏:避免在构造函数中创建闭包,防止意外保留无用引用。
3. 严格模式下的 this
保护
"use strict";
function Person() {
console.log(this); // 未使用 new 时,this 为 undefined
}
Person(); // 报错:Cannot set property 'name' of undefined
四、new
的应用场景与陷阱
1. 适用场景
-
自定义类型:需要明确类型标识(如
instanceof
检测)。 -
共享方法:通过原型链实现方法复用。
-
框架开发:如 React 组件类(
class Component
基于new
)。
2. 常见陷阱
-
忘记使用
new
:function Person(name) { this.name = name; } const p = Person("Alice"); // this 指向全局(或 undefined)
解决方案:构造函数内强制
new
调用:function Person(name) { if (!(this instanceof Person)) { return new Person(name); } this.name = name; }
五、总结
-
new
的核心作用:基于构造函数创建实例,绑定原型链,初始化对象属性。 -
底层四步:创建空对象 → 绑定原型 → 执行构造函数 → 处理返回值。
-
内存模型:实例属性独立存储,原型方法共享于原型对象。
-
最佳实践:方法定义在原型,避免构造函数返回非对象值,使用 ES6
class
简化代码。

GitCode 天启AI是一款由 GitCode 团队打造的智能助手,基于先进的LLM(大语言模型)与多智能体 Agent 技术构建,致力于为用户提供高效、智能、多模态的创作与开发支持。它不仅支持自然语言对话,还具备处理文件、生成 PPT、撰写分析报告、开发 Web 应用等多项能力,真正做到“一句话,让 Al帮你完成复杂任务”。
更多推荐
所有评论(0)