JavaScript new绑定实例化时的内存分配

作者:技术拾光时间:2026-07-06 08:23:11浏览:342

很多开发者刚接触 JavaScript 时,总觉得 new 关键字像个黑盒 —— 输入一个构造函数,出来一个对象,中间发生了什么?其实 JavaScript new 关键字内部执行流程 非常清晰,一共就四步,每一步都有明确的职责。本文就把它拆开细看,帮助理解 new 实例化过程 的内存分配与原型链机

很多开发者刚接触 JavaScript 时,总觉得 new 关键字像个黑盒 —— 输入一个构造函数,出来一个对象,中间发生了什么?其实 JavaScript new 关键字内部执行流程 非常清晰,一共就四步,每一步都有明确的职责。本文就把它拆开细看,帮助理解 new 实例化过程 的内存分配与原型链机制。

JavaScript new绑定实例化时的内存分配

在 JavaScript 中,new 操作符 在执行构造函数时,内存分配并非“一次性拍板”,而是分阶段、有策略地在堆内存中完成的。它不会直接分配一个完整对象,而是先预留空间,再逐步填充,并通过原型链共享方法,从而避免重复开销。下面按步骤展开说明 new 底层原理。

堆内存中创建空对象

当你执行 new Person("Alice") 时,第一步是向堆内存申请一块新空间,生成一个空对象({})。这个对象本身不含任何自有属性,但它已经具备了基本对象结构,包括隐式原型 __proto__ —— 注意,此刻 __proto__ 默认指向 Object.prototype,尚未与构造函数关联。也就是说,第一步只是“搭了个空架子”,后面的步骤才往里填内容。这是 JavaScript 实例化过程 中内存分配的起始点。

原型链接改写,不额外占用内存

第二步把刚刚那个空对象的 __proto__ 指针重定向到 Person.prototype。这一步仅仅是修改引用地址,不分配新内存。如何理解?

  • __proto__ 本质上是个指针,指向已存在的原型对象(这个原型对象通常在堆中早于实例就存在,由构造函数定义时生成)
  • 原型对象里的方法(如 Person.prototype.sayName)被所有实例共享,不会随每个实例重复复制
  • 实例自身只存储“独有数据”,比如 name、age 等自有属性

因此这一步几乎不占额外内存,只是把已有的原型链接挂上。在 new 绑定 过程中,这正是实现方法复用的关键。

构造函数执行时填充实例数据

第三步调用 Person.call(新对象, "Alice"),此时 this 指向刚分配的堆内存地址。构造函数内部对 this.name = "Alice" 的赋值,就是往那块堆空间里写入字段:

  • 字符串 "Alice" 存在堆中(或字符串常量池),name 属性保存的是对该字符串的引用
  • 如果构造函数内定义了函数(如 this.getName = function(){...}),这个函数也会被创建在堆中,而且每个实例都拥有一份 —— 这是常见的内存浪费点
  • 推荐把方法挂到 prototype 上,让实例通过原型链访问,这样只存一份,节省堆空间

这一步清晰展示了:实例负责保存自己的专属数据,共享行为则交给原型。这也是 JavaScript 内存分配 策略的典型体现。

返回对象始终指向堆中实体

第四步判断构造函数的返回值:

  • 若返回的是对象(包含 null),则直接返回该对象(可能会覆盖原本创建的实例)
  • 否则,返回最初分配的那个堆内存对象

无论哪种情况,最终变量持有的都是一个堆内存地址引用,栈中只存这个地址,而不是整个对象数据。所以当你执行 let p = new Person("Alice") 时,p 变量里存的就是那个堆地址,对象本身始终待在堆中。理解 new 关键字执行步骤,就能清楚掌握 JavaScript 中实例化与原型链的底层机制。