引言
在 JavaScript 中,new
操作符是实现面向对象编程的核心机制之一。本文将从原理层面对 new
操作符进行深度剖析,探讨其工作机制、内部实现和实际应用场景。无论您是 JavaScript 初学者还是资深开发者,都能从本文获得以下知识和技能:
- 理解
new
操作符的底层执行原理 - 掌握手动模拟
new
操作符的完整实现方案 - 学会处理构造函数中的返回值边界情况
- 理解原型链继承的核心工作机制
- 避免常见
new
操作符陷阱与反模式 - 深入认识
class
语法糖的本质 - 掌握高级应用场景下的性能优化策略
让我们共同踏上这场对 JavaScript 对象创建机制的探索之旅!
文章大纲
new
操作符基本概念- 对象创建的两种方式
- 构造函数定义与约定
new
操作符执行原理- 内部执行步骤详解
- 内存模型图解
- 原型链构建过程
- 手动实现
new
操作符- 分步实现方案
- 边界条件处理
- 完整参考实现
- 构造函数返回值处理
- 基本类型返回值
- 对象类型返回值
- ES6类中的特殊行为
- 原型继承机制解析
prototype
作用- 原型链查找图解
Object.create
底层实现
new
操作符的现代实践- ES6类与构造函数对比
- 工厂函数替代方案
- 性能优化策略
- 常见陷阱与最佳实践
- 遗漏
new
的解决方案 - 原型污染风险
- 类变量共享问题
- 遗漏
- 总结与展望
1. new
操作符基本概念
1.1 对象创建的两种方式
JavaScript 提供了两种对象创建方式:
// 字面量语法(Literal Syntax)
const person = {name: 'Alice',greet() {console.log(`Hello, I'm ${this.name}`);}
};// 构造函数模式(Constructor Pattern)
function Person(name) {this.name = name;
}
Person.prototype.greet = function() {console.log(`Hello, I'm ${this.name}`);
};const alice = new Person('Alice');
虽然对象字面量语法简洁高效,但当我们需要创建多个相似对象时,使用 new
结合构造函数的方式更符合 DRY(Don’t Repeat Yourself) 原则。
1.2 构造函数的约定
JavaScript 中的构造函数遵循特定约定,便于开发者识别:
- 首字母大写命名(大驼峰式)
- 通过
new
操作符调用 - 使用
this
初始化实例属性 - 在原型上定义共享方法
function Animal(species) {// 实例属性初始化this.species = species;this.isAlive = true;
}// 原型共享方法
Animal.prototype.describe = function() {return `${this.species} is ${this.isAlive ? 'alive' : 'dead'}`;
};const tiger = new Animal('Tiger');
2. new
操作符执行原理
2.1 内部执行步骤详解
当执行 new Constructor(...args)
时,JavaScript 引擎内部按顺序执行以下步骤:
1. 创建一个新对象: 在内存中创建一个全新的、空白的普通 JavaScript 对象 ({}
或 new Object()
的结果)。
2. 链接原型链: 将这个新创建的对象的内部 [[Prototype]]
(即 __proto__
属性) 指向构造函数的 prototype
属性所指向的对象。这步是关键!它建立了新对象和构造函数原型之间的链接,使得新对象可以访问构造函数原型上定义的所有属性和方法。
3. 绑定 this
: 将构造函数内部的 this
关键字绑定到步骤 1 创建的这个新对象上。这样在构造函数内部通过 this
添加的属性或方法,实际上就添加到了这个新对象上。
4. 执行构造函数: 调用构造函数 (Constructor()
函数体开始执行)。构造函数内部的代码通常使用 this
来初始化新对象的属性和方法。
5. 返回新对象: 除非构造函数自身返回另一个对象(非 null 的对象或函数),否则 new
表达式会自动返回步骤 1 创建的那个新对象。如果构造函数返回了一个非 null 对象(包括数组、函数、自定义对象等),那么这个返回的对象就会替代最初创建的那个对象作为 new
表达式的结果(这种情况下,步骤 1 创建的对象可能就会被垃圾回收)。如果构造函数返回原始值(undefined
, null
, number
, string
, boolean
, symbol
, bigint
),返回值会被忽略,步骤 1 创建的对象仍然会被返回。
关键特性:
- 原型继承:
new
是 JavaScript 实现基于原型的继承的核心机制。通过将实例的[[Prototype]]
链接到构造函数的prototype
,实例可以访问和“继承”定义在构造函数原型上的属性和方法。 - 隔离状态: 每次调用
new
都会创建一个全新的对象实例。即使多个实例由同一个构造函数创建,它们各自的属性(在构造函数内部通过this
添加的属性)也是相互独立的。
2.2 内存模型图解
下列模型展示了 new
操作执行时的对象之间的关系:
关注点:
- 每个构造函数通过
prototype
引用原型对象(同样,原型对象通过constructor
关联构造函数,上图没有展示)。 prototype
只会出现在构造函数中,非构造器的对象是没有prototype
属性的。- 内部属性
[[Prototype]]
用来链接原型对象,形成原型链,[[Prototype]]
即__proto__
属性。 Function
的prototype
和[[Prototype]]
同时指向Function.prototype
,因为Function.prototype
定义了所有函数对象的共享属性与方法。
2.3 原型链构建过程
使用 new
操作符时创建的原型链关系如下:
当访问实例属性时,JavaScript 引擎会沿着原型链向上查找:
- 首先检查实例自身属性
- 若不存在则检查原型对象
- 直到找到属性或到达原型链顶端(
null
)
3. 手动实现 new
操作符
3.1 分步实现方案
我们通过 createInstance
函数模拟 new
操作符的行为:
function createInstance(Constructor, ...args) {// Step 1: 创建新对象const obj = {};// Step 2: 连接原型链obj.__proto__ = Constructor.prototype;// Step 3: 执行构造函数const result = Constructor.apply(obj, args);// Step 4: 处理返回值return typeof result === 'object' ? result : obj;
}// 使用示例
function Person(name) {this.name = name;
}
const john = createInstance(Person, 'John');
console.log(john.name); // 输出: John
3.2 边界条件处理
实际生产环境需要考虑更多边界情况:
function createInstance(Constructor, ...args) {// 处理非构造函数调用if (typeof Constructor !== 'function') {throw new TypeError(Constructor + ' is not a constructor');}// 创建原型链连接的对象(支持ES5+环境)const obj = Object.create(Constructor.prototype);// 执行构造函数const result = Constructor.apply(obj, args);// 判断返回值类型const isObject = result !== null && typeof result === 'object';const isFunction = typeof result === 'function';return isObject || isFunction ? result : obj;
}
3.3 完整参考实现(兼容现代规范)
考虑构造函数可能返回 Symbol
、null
等特殊值:
function createInstance(Constructor, ...args) {if (typeof Constructor !== 'function') {throw new TypeError(`${String(Constructor)} is not a constructor`);}const proto = Constructor.prototype;const obj = Object.create(proto !== null && typeof proto === 'object' ? proto : Object.prototype);const result = Reflect.apply(Constructor, obj, args);// ES规范:构造函数返回非对象时自动返回新对象return result !== null && (typeof result === 'object' || typeof result === 'function')? result : obj;
}
4. 构造函数返回值处理
4.1 返回值类型影响
function Normal() {this.value = 'normal';
}function ReturnPrimitive() {this.value = 'primitive';return 42; // 基本类型返回值
}function ReturnObject() {this.value = 'overridden';return { custom: 'object' };
}console.log(new Normal().value); // 'normal'
console.log(new ReturnPrimitive()); // 实例对象 { value: 'primitive' }
console.log(new ReturnObject()); // { custom: 'object' } (不是 ReturnObject 实例)
4.2 ES6 类中的特殊行为
ES6 class
语法对返回值有更严格的限制:
class StrictClass {constructor() {return 42; // 基本类型会被忽略}
}console.log(new StrictClass() instanceof StrictClass); // trueclass ReturnObjectClass {constructor() {return { custom: 'object' };}
}console.log(new ReturnObjectClass() instanceof ReturnObjectClass); // false
5. 原型继承机制解析
原型链 (Prototype Chain) 是 JavaScript 实现基于原型的继承 (Prototypal Inheritance) 的核心机制。它让对象能够访问到自身不存在的属性和方法,沿着一个由对象和原型链接而成的链条向上查找。理解原型链对掌握 JavaScript 的面向对象编程、继承、属性和方法查找至关重要。
5.1 核心概念
-
[[Prototype]]
(__proto__
) 属性:- 每一个 JavaScript 对象(包括函数对象,因为函数也是对象)在创建时,都会内置一个叫做
[[Prototype]]
的内部属性(在 ES5 标准中引入)。这个属性对外不可直接访问。 - 为了方便调试和访问,大多数浏览器环境提供了一个非标准但被广泛实现的属性
__proto__
来访问对象的[[Prototype]]
。 - 重要:
[[Prototype]]
指向的是对象的原型对象。
- 每一个 JavaScript 对象(包括函数对象,因为函数也是对象)在创建时,都会内置一个叫做
-
prototype
属性:- 只有函数对象才拥有
prototype
属性。 - 当你使用
new
操作符调用这个函数(作为构造函数)时,新创建对象的[[Prototype]]
(即__proto__
) 会指向该函数的prototype
属性所指向的对象。 Constructor.prototype
本身也是一个对象,它通常被设计用来存放该构造函数创建的所有实例对象共享的属性和方法。function Foo() {}
创建的实例obj = new Foo()
,其obj.__proto__ === Foo.prototype
。
- 只有函数对象才拥有
5.2 prototype
核心作用
关键原则:构造函数的 prototype
属性被所有实例共享,这提供了:
- 方法共享(节省内存)
- 动态添加方法的能力
- 继承机制的实现基础
5.3 Object.create
原理
Object.create()
核心思想就是 显式地创建一个新对象,并设置其内部原型链 ([[Prototype]]
) 指向我们指定的对象,而不需要通过构造函数的 new
操作。
核心原理
Object.create(proto[, propertiesObject])
-
proto
参数: 新创建对象的[[Prototype]]
(即__proto__
)将被设置为proto
。这是唯一必需的参数。- 如果
proto
是null
,新创建的对象将没有原型([[Prototype]] = null
),它是一个非常纯净的对象,不会继承任何属性和方法(包括Object.prototype
上的toString
,hasOwnProperty
等)。 - 如果
proto
是一个对象(包括Object
,Array
, 自定义构造函数的prototype
或其他对象),那么新对象的原型链就直接指向这个对象。
- 如果
-
propertiesObject
参数 (可选): 一个对象,用来为新创建的对象添加自身的可枚举属性(就像通过Object.defineProperties()
添加的一样)。这些属性描述符(configurable
,enumerable
,writable
,value
,get
,set
)直接定义在新对象自身上,而不在其原型链上。
手动模拟实现 (Polyfill)
理解原理最好的方式之一是手动模拟。在 ES5 之前或在不支持的旧环境中(严格来说,__proto__
是非标准的,但实际可用),我们可以这样模拟 Object.create
的核心功能(只处理第一个参数 proto
):
if (typeof Object.create !== 'function') {Object.create = function(proto) {// 1. 参数类型检查 (简化版,真实实现会更严谨)if (typeof proto !== 'object' && typeof proto !== 'function' && proto !== null) {throw new TypeError('Object prototype may only be an Object or null');}// 2. 创建一个空的构造函数function F() {} // 这个函数体是空的,不会执行任何初始化逻辑// 3. **核心操作**:将构造函数的 prototype 指向传入的 proto 对象F.prototype = proto; // 关键步骤:建立原型链接的桥梁// 4. 使用 new 操作符创建新对象。根据 new 规则:// a. 创建一个新对象 {}// b. 这个新对象的 [[Prototype]] (__proto__) 会被设置为 F.prototype,也就是我们传入的 `proto`// c. 执行 F 函数体(空函数,什么也不做)// d. 返回这个新对象return new F();};
}
关键原理分析
- 利用构造函数原型链机制: 这个模拟实现巧妙利用了
new
操作符的第 2 步(链接原型链)。它通过将空函数F
的prototype
属性设置为目标原型对象proto
,然后new F()
创建的新对象自然将其[[Prototype]]
指向了F.prototype
,从而指向了我们指定的proto
。 - 与
new
的区别:- 构造函数调用:
Object.create
本身不会调用任何构造函数(模拟中F
是空的)。它只关心建立原型链。而new Constructor()
会执行Constructor
函数体用于初始化。 - 原型来源:
Object.create(proto)
直接设置新对象的原型为proto
。new Constructor()
设置新对象的原型为Constructor.prototype
。 - 灵活性:
Object.create
可以创建原型为任意指定对象(包括null
)的新对象,非常灵活。new
要求是一个函数(构造函数)。 - 纯净对象 (
null
原型):Object.create(null)
是创建完全没有继承任何属性(包括Object.prototype
)的对象的唯一安全、标准方式。new Object()
或{}
创建的对象原型都是Object.prototype
。
- 构造函数调用:
-
propertiesObject
的实现: 模拟实现中未处理该参数。标准实现中,它会使用Object.defineProperties(newObj, propertiesObject)
将属性描述符定义到新对象newObj
自身上。这些属性不是从proto
继承来的,而是新对象本身的属性。
核心用途
- 纯粹的原型继承: 在不定义构造函数的情况下直接基于某个对象创建新实例,共享其方法和部分属性。
- 创建无原型的对象 (
Object.create(null)
):- 性能优化:移除
Object.prototype
上的方法,适合作为高性能键值对(字典)。 - 避免命名冲突:防止无意中继承或覆盖
Object.prototype
上的属性。
- 性能优化:移除
- 替代
new
的复杂原型设置: 当需要灵活设置新对象的原型(比如直接指向另一个非构造函数prototype
的普通对象)时。 - ES5 及之后标准继承的基础: ES6
class
的extends
在底层依赖于此机制设置子类的原型链。 - 属性添加 (可选参数): 配合属性描述符精确控制新对象的自身属性。
总结: Object.create()
的原理是直接操作原型链,通过显式指定一个新创建对象的 [[Prototype]]
来实现。它绕过了构造函数的初始化过程,提供了一种更灵活、底层的对象创建方式,是实现原型继承和创建特殊类型对象(如无原型对象)的关键工具。它的核心就是建立新对象与被指定为原型的对象之间的链接。
实际 new
操作的第一步可以用 Object.create
表示:
function Animal() {}
const proto = Animal.prototype;const obj = Object.create(proto);
// 等价于
const obj = {};
Object.setPrototypeOf(obj, proto); // ES6 写法
5.4 原型链查找性能优化
原型链查找是 JavaScript 性能优化中一个需要关注的潜在瓶颈。
为什么原型链查找可能影响性能?
- 属性搜索成本: 当访问一个对象的属性时,JavaScript 引擎需要沿原型链逐级搜索,直到找到该属性或到达
null
(终点)。链越长,搜索层级越多,查找时间越长,尤其对于深层链顶部的属性。 - Hidden Classes (V8等引擎): 现代引擎使用 Hidden Classes (或 Shapes, Map) 来优化对象访问。
- 对象的 Hidden Class 记录了对象的布局信息(有哪些自身属性及其偏移量)。
- 当访问一个自身的、在 Hidden Class 中明确记录的属性时,速度极快(几乎接近静态语言)。
- 问题在于原型链查找:
- 访问不在自身 Hidden Class 中的属性(在原型链上)时,引擎需要:
- 检查当前对象的 Hidden Class。
- 如果找不到,跳到原型链的下一个对象 (
[[Prototype]]
)。 - 检查_那个_对象的 Hidden Class。
- 重复步骤 2-3,直到找到属性或到达链尾。
- 这个过程涉及到多个对象的 Hidden Class 查找和上下文的切换,比访问自身属性慢得多。
- 访问不在自身 Hidden Class 中的属性(在原型链上)时,引擎需要:
- 多态与内联缓存失效:
- 引擎使用 内联缓存 (Inline Caches, ICs) 来记住之前访问过的属性和它在哪找到的(自身还是哪个原型的 Hidden Class)。
- 如果同一个位置的代码访问原型链上的属性,但
this
指向的对象来自不同的原型分支(导致属性出现在不同的 Hidden Class 里),内联缓存就会变得多态 (Polymorphic) 甚至巨态 (Megamorphic),其性能会显著下降,因为它需要处理多种情况。 - 如果每次查找的 Hidden Class 都不同(巨态),缓存几乎无用,每次查找都接近于全量搜索。
优化策略与最佳实践
-
扁平化原型链 (Minimize Prototype Chain Depth):
- 这是最核心、最有效的策略。避免过深、过宽、复杂的原型链。
- 仔细评估继承层次:是否真的需要多层深度继承?组合(Composition)模式(将功能对象作为成员引入)通常是更好的选择。
- ES6
class
通常有较浅的原型链(实例->类原型->父类原型->Object.prototype->null),避免过深层级。 - 对于共享方法,考虑将它们放在第一层(类原型)上。
-
优先在对象自身存储频繁访问的属性 (Promote Frequently Accessed Properties to Own Properties):
- 如果一个属性(尤其是数据属性)在对象的生命周期内会被非常高频率地访问,考虑在构造函数中初始化它作为自身属性 (
this.prop = val
) 或使用类字段(ES2022+)。 - 自身属性的访问速度最快(直接通过 Hidden Class 定位)。不要仅仅为了“省点内存”而把高频访问的数据放在原型链深处。
优化前:
function GameObject() {} GameObject.prototype.position = { x: 0, y: 0 }; // 位置对象放在原型上function Player() {} Player.prototype = Object.create(GameObject.prototype); //... const player = new Player(); // 高频访问位置 x function renderLoop() {// 每次都在原型链上查找 position,再从 position 找 x (又是链查找)player.position.x += velocity.x;// ...其他渲染逻辑 }
优化后:
function GameObject() {this.position = { x: 0, y: 0 }; // ❌ 还是不对! (优化点1) }function Player() {GameObject.call(this); // 继承位置this.x = this.position.x; // ✅ 自身属性 (优化点2)this.y = this.position.y;// 或者更彻底 (优化点3): 如果position只需存储xy,直接:// this.x = 0;// this.y = 0; } Player.prototype = Object.create(GameObject.prototype); //... const player = new Player(); function renderLoop() {// 直接访问 player.x (自身属性,超快!)player.x += velocity.x; }
- 如果一个属性(尤其是数据属性)在对象的生命周期内会被非常高频率地访问,考虑在构造函数中初始化它作为自身属性 (
-
谨慎使用
delete
操作符:delete obj.prop
会改变对象的 Hidden Class,使得引擎为删除了属性的对象创建新的 Hidden Class。- 频繁创建和销毁 Hidden Class 会导致性能下降(尤其是在热代码路径中)。
- 替代方案: 将不用的属性设置为
null
或undefined
(或初始化为无效值),而不是删除。这不会改变 Hidden Class。避免在运行时动态添加或删除关键属性。
-
缓存查找结果 (Caching Lookup Results):
-
如果某个原型链上的属性在同一个作用域/函数中被多次访问,且无法提升为自身属性,可以将其缓存到局部变量。
未缓存:
for (let i = 0; i < 1000; i++) {// 每次循环都要去原型链查找 `expensiveCalculation`result = obj.someProto.expensiveCalculation(obj.data); }
缓存后:
const calcFn = obj.someProto.expensiveCalculation; // 缓存函数引用 const data = obj.data; // 如果 data 也需要查找,也缓存 for (let i = 0; i < 1000; i++) {result = calcFn(data); // 只访问自身作用域中的变量,很快 }
-
-
尽量在构造函数中初始化所有属性 (Pre-initialize All Properties in Constructor):
- 一次性在构造函数中声明和初始化对象的所有预期属性(设为初始值如
undefined
,null
,0
,''
),即使这些值稍后才被设置。这有助于引擎创建出最“稳定”的 Hidden Class,避免后续属性添加导致 Hidden Class 频繁转换。 - 不要后续通过
obj.newProp = value
在方法中随意添加全新属性。
- 一次性在构造函数中声明和初始化对象的所有预期属性(设为初始值如
-
使用
Object.seal
/Object.freeze
(谨慎使用):- 在对象初始化完成后使用
Object.seal(obj)
或Object.freeze(obj)
。 Object.seal()
:防止添加新属性,并将所有现有属性标记为不可配置(configurable: false)。防止属性被删除。Object.freeze()
:在seal
基础上,防止任何现有属性的值被修改(只读)。- 它们有助于“锁定”对象的 Hidden Class,因为对象结构在之后不会被更改。引擎可以进行更激进的优化。但注意这极大地限制了对象的灵活性,通常只用于完全初始化好、不再更改的小型配置对象或只读数据容器。不适合需要动态变化的大多数业务对象。
- 在对象初始化完成后使用
-
优先使用 ES6
class
语法:- 现代引擎对 ES6
class
有更好的优化支持。 class
强制方法定义在原型上,属性初始化在构造函数中(或使用类字段),促使更优化的对象布局。- 清晰的继承结构 (
extends
) 比手写修改__proto__
或Object.setPrototypeOf
更容易被引擎理解和优化。 - 避免使用
Object.setPrototypeOf()
: 它比Object.create()
更慢,而且会强制改变已有对象的 Hidden Class 和相关联的内联缓存,导致严重的性能回退。在创建时就确定好原型链(通过new
或Object.create
)。
- 现代引擎对 ES6
-
分析和测量 (Profile and Measure):
- 性能优化最大的准则是:不要过早优化!不要臆测瓶颈在哪里!
- 使用浏览器开发者工具(如 Chrome DevTools 的 Performance Profiler 和 Memory Profiler)、Node.js 的性能分析工具来定位热点代码。
- 测量改变原型链结构、属性访问方式后的实际性能影响。 优化策略的实际收益取决于具体使用场景(链深度、访问频率、引擎)。
总结: 原型链查找的性能优化关键在于最小化链的深度、将高频访问的数据提升为自身属性、避免运行时破坏对象的 Hidden Class 以及善用缓存。记住现代引擎如 V8 的 Hidden Class 和 ICs 机制是理解这些优化的基础。但同时要警惕过度优化带来的代码复杂性和维护成本,始终依赖于精准的性能分析结果来指导优化方向。对于绝大多数代码来说,遵循 ES6 class
语法和良好的设计实践(如组合优于深层继承)已经能提供相当不错的性能。
6. new
操作符的现代实践
6.1 ES6 类与构造函数的关系
class Animal {constructor(name) {this.name = name;}speak() {console.log(`${this.name} makes a noise.`);}
}// 本质上还是构造函数
console.log(typeof Animal); // 'function'// 构造函数的prototype不可枚举
const descriptor = Object.getOwnPropertyDescriptor(Animal, 'prototype');
console.log(descriptor.enumerable); // false
6.2 工厂函数替代方案
function createPerson(name) {// 直接创建对象const obj = {name,// 方法直接定义(非共享)greet() {console.log(`Hello, I'm ${this.name}`);}};// 或者复用原型const proto = {greet() {console.log(`Hello, I'm ${this.name}`);}};return Object.assign(Object.create(proto), { name });
}
6.3 性能优化策略
优化大量对象创建场景:
// 方法提升到原型
function Optimized(size) {this.size = size;
}// 避免每次创建时重新定义方法
Optimized.prototype.calculate = function() {return this.size * 100;
};// 对象池技术
const pool = [];
function createFromPool() {return pool.length ? pool.pop() : new Optimized();
}function releaseToPool(obj) {pool.push(obj);
}
7. 常见陷阱与最佳实践
7.1 遗漏 new
的解决方案
function User(name) {if (!(this instanceof User)) {return new User(name); // 安全防护}this.name = name;
}// ES6新特性:new.target
function ModernUser(name) {if (!new.target) {throw new Error('Must call with new');}this.name = name;
}
7.2 原型污染风险
function SafeObject() {}// 避免直接修改内置原型
SafeObject.prototype = Object.create(null);// 创建纯净对象
const pureObj = Object.create(null);
7.3 类变量共享问题
function SharedCounter() {this.count = 0;
}
SharedCounter.prototype.increment = function() {this.count++;
}// 错误使用静态属性作为"类变量"
SharedCounter.total = 0; // 安全用法// 正确解决方案:工厂函数
function createCounter() {let privateCount = 0;return {increment() {privateCount++;},getCount() {return privateCount;}};
}
8. 总结与展望
new
操作符是 JavaScript 面向对象编程的基石,其背后蕴含着原型继承的精妙设计。通过本文,您已深入理解:
new
操作符的四步核心流程:创建 → 链接 → 初始化 → 返回- 原型链的动态绑定特性及其性能影响
- 处理构造函数的返回值边界条件
ES6 class
语法与构造函数的等价转换- 避免常见陷阱的防护策略
未来发展趋势中,静态类型检查(TypeScript)、不可变数据结构和函数式编程范式正逐渐改变对象创建模式。但作为 JavaScript 核心特性,深入理解 new
操作符仍至关重要,它帮助我们构建高效、可维护的复杂应用。
扩展阅读
- ECMAScript 规范 - new 操作符定义
- MDN - new 操作符文档
- JavaScript 原型继承深度指南
- V8 引擎中的对象表示