JavaScript 对象创建:new 操作符全解析

引言

在 JavaScript 中,new 操作符是实现面向对象编程的​​核心机制​​之一。本文将从原理层面对 new 操作符进行深度剖析,探讨其工作机制、内部实现和实际应用场景。无论您是 JavaScript 初学者还是资深开发者,都能从本文获得以下知识和技能:

  1. 理解 new 操作符的​​底层执行原理​
  2. 掌握手动模拟 new 操作符的​​完整实现方案​
  3. 学会处理构造函数中的​​返回值边界情况​
  4. 理解原型链继承的​​核心工作机制​
  5. 避免常见 new 操作符​​陷阱与反模式​
  6. 深入认识 class 语法糖的本质
  7. 掌握高级应用场景下的性能优化策略

让我们共同踏上这场对 JavaScript 对象创建机制的探索之旅!

文章大纲

  1. new 操作符基本概念
    • 对象创建的两种方式
    • 构造函数定义与约定
  2. new 操作符执行原理
    • 内部执行步骤详解
    • 内存模型图解
    • 原型链构建过程
  3. 手动实现 new 操作符
    • 分步实现方案
    • 边界条件处理
    • 完整参考实现
  4. 构造函数返回值处理
    • 基本类型返回值
    • 对象类型返回值
    • ES6类中的特殊行为
  5. 原型继承机制解析
    • prototype 作用
    • 原型链查找图解
    • Object.create 底层实现
  6. new 操作符的现代实践
    • ES6类与构造函数对比
    • 工厂函数替代方案
    • 性能优化策略
  7. 常见陷阱与最佳实践
    • 遗漏 new 的解决方案
    • 原型污染风险
    • 类变量共享问题
  8. 总结与展望

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 添加的属性)也是相互独立的。
new 操作开始
创建新对象 obj
设置
obj.__proto__ = Constructor.prototype
执行
Constructor.apply(obj, args)
Constructor 是否返回对象?
返回该对象
返回新对象 obj

2.2 内存模型图解

下列模型展示了 new 操作执行时的对象之间的关系:

原型链的对象关系图

关注点

  • 每个构造函数通过 prototype 引用原型对象(同样,原型对象通过 constructor 关联构造函数,上图没有展示)。
  • prototype 只会出现在构造函数中,非构造器的对象是没有 prototype 属性的。
  • 内部属性 [[Prototype]] 用来链接原型对象,形成原型链,[[Prototype]]__proto__ 属性。
  • Functionprototype[[Prototype]] 同时指向 Function.prototype,因为 Function.prototype 定义了所有函数对象的共享属性与方法。

2.3 原型链构建过程

使用 new 操作符时创建的原型链关系如下:

Object.prototype
Animal.prototype
New Instance
[[Prototype]]
[[Prototype]]
toString method
Animal.prototype
describe method
new Animal

当访问实例属性时,JavaScript 引擎会沿着原型链向上查找:

  1. 首先检查实例自身属性
  2. 若不存在则检查原型对象
  3. 直到找到属性或到达原型链顶端(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 完整参考实现(兼容现代规范)

考虑构造函数可能返回 Symbolnull 等特殊值:

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 核心概念

  1. [[Prototype]] (__proto__) 属性:​

    • 每一个 JavaScript 对象(包括函数对象,因为函数也是对象)在创建时,都会内置一个叫做 [[Prototype]] 的内部属性(在 ES5 标准中引入)。这个属性对外不可直接访问。
    • 为了方便调试和访问,大多数浏览器环境提供了一个非标准但被广泛实现的属性 __proto__ 来访问对象的 [[Prototype]]
    • ​重要:​[[Prototype]] 指向的是对象的​​原型对象​​。
  2. prototype 属性:​

    • ​只有函数对象才拥有 prototype 属性。​
    • 当你使用 new 操作符调用这个函数(作为构造函数)时,新创建对象的 [[Prototype]] (即 __proto__) 会指向该函数的 prototype 属性所指向的对象。
    • Constructor.prototype 本身也是一个对象,它通常被设计用来存放该构造函数创建的​​所有实例对象共享的属性和方法​​。
    • function Foo() {} 创建的实例 obj = new Foo(),其 obj.__proto__ === Foo.prototype

5.2 prototype 核心作用

prototype
[[Prototype]]
[[Prototype]]
Constructor
Prototype
Instance1
Instance2

​关键原则​​:构造函数的 prototype 属性被所有实例共享,这提供了:

  • 方法共享(节省内存)
  • 动态添加方法的能力
  • 继承机制的实现基础

5.3 Object.create 原理

Object.create() 核心思想就是 ​​显式地创建一个新对象,并设置其内部原型链 ([[Prototype]]) 指向我们指定的对象​​,而不需要通过构造函数的 new 操作。

核心原理

Object.create(proto[, propertiesObject])

  1. proto 参数:​​ 新创建对象的 [[Prototype]](即 __proto__)将被设置为 proto。这是唯一必需的参数。

    • 如果 protonull,新创建的对象将没有原型([[Prototype]] = null),它是一个非常纯净的对象,不会继承任何属性和方法(包括 Object.prototype 上的 toString, hasOwnProperty 等)。
    • 如果 proto 是一个对象(包括 Object, Array, 自定义构造函数的 prototype 或其他对象),那么新对象的原型链就直接指向这个对象。
  2. 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();};
}

关键原理分析

  1. ​利用构造函数原型链机制:​​ 这个模拟实现巧妙利用了 new 操作符的第 2 步(链接原型链)。它通过将空函数 Fprototype 属性设置为目标原型对象 proto,然后 new F() 创建的新对象自然将其 [[Prototype]] 指向了 F.prototype,从而指向了我们指定的 proto
  2. ​与 new 的区别:​
    • ​构造函数调用:​Object.create 本身​​不会调用任何构造函数​​(模拟中 F 是空的)。它只关心建立原型链。而 new Constructor()​会执行 Constructor 函数体​​用于初始化。
    • ​原型来源:​Object.create(proto) 直接设置新对象的原型为 protonew Constructor() 设置新对象的原型为 Constructor.prototype
    • ​灵活性:​Object.create 可以创建​​原型为任意指定对象​​(包括 null)的新对象,非常灵活。new 要求是一个函数(构造函数)。
    • ​纯净对象 (null 原型):​Object.create(null) 是创建完全没有继承任何属性(包括 Object.prototype)的对象的​​唯一​​安全、标准方式。new Object(){} 创建的对象原型都是 Object.prototype
  3. propertiesObject 的实现:​​ 模拟实现中未处理该参数。标准实现中,它会使用 Object.defineProperties(newObj, propertiesObject) 将属性描述符定义到新对象 newObj 自身上。这些属性不是从 proto 继承来的,而是新对象本身的属性。
核心用途
  1. ​纯粹的原型继承:​​ 在不定义构造函数的情况下直接基于某个对象创建新实例,共享其方法和部分属性。
  2. ​创建无原型的对象 (Object.create(null)):​
    • 性能优化:移除 Object.prototype 上的方法,适合作为高性能键值对(字典)。
    • 避免命名冲突:防止无意中继承或覆盖 Object.prototype 上的属性。
  3. ​替代 new 的复杂原型设置:​​ 当需要灵活设置新对象的原型(比如直接指向另一个非构造函数 prototype 的普通对象)时。
  4. ​ES5 及之后标准继承的基础:​​ ES6 classextends 在底层依赖于此机制设置子类的原型链。
  5. ​属性添加 (可选参数):​​ 配合属性描述符精确控制新对象的自身属性。

​总结:​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 性能优化中一个需要关注的潜在瓶颈。

为什么原型链查找可能影响性能?
  1. ​属性搜索成本:​​ 当访问一个对象的属性时,JavaScript 引擎需要​​沿原型链逐级搜索​​,直到找到该属性或到达 null(终点)。​​链越长,搜索层级越多,查找时间越长​​,尤其对于深层链顶部的属性。
  2. ​Hidden Classes (V8等引擎):​​ 现代引擎使用 ​​Hidden Classes (或 Shapes, Map)​​ 来优化对象访问。
    • 对象的 Hidden Class 记录了​​对象的布局信息​​(有哪些自身属性及其偏移量)。
    • 当访问一个​​自身的、在 Hidden Class 中明确记录的​​属性时,速度极快(几乎接近静态语言)。
    • ​问题在于原型链查找:​
      • 访问不在自身 Hidden Class 中的属性(在原型链上)时,引擎需要:
        1. 检查当前对象的 Hidden Class。
        2. 如果找不到,跳到原型链的下一个对象 ([[Prototype]])。
        3. 检查_那个_对象的 Hidden Class。
        4. 重复步骤 2-3,直到找到属性或到达链尾。
      • 这个过程涉及到多个对象的 Hidden Class 查找和上下文的切换,比访问自身属性慢得多。
  3. ​多态与内联缓存失效:​
    • 引擎使用 ​​内联缓存 (Inline Caches, ICs)​​ 来记住之前访问过的属性和它在哪找到的(自身还是哪个原型的 Hidden Class)。
    • 如果​​同一个位置的代码​​访问原型链上的属性,但​this指向的对象来自不同的原型分支​​(导致属性出现在不同的 Hidden Class 里),内联缓存就会变得​​多态 (Polymorphic)​​ 甚至​​巨态 (Megamorphic)​​,其性能会显著下降,因为它需要处理多种情况。
    • 如果每次查找的 Hidden Class 都不同(巨态),缓存几乎无用,每次查找都接近于全量搜索。
优化策略与最佳实践
  1. ​扁平化原型链 (Minimize Prototype Chain Depth):​

    • 这是​​最核心、最有效​​的策略。​​避免过深、过宽、复杂的原型链。​
    • 仔细评估继承层次:是否真的需要多层深度继承?组合(Composition)模式(将功能对象作为成员引入)通常是更好的选择。
    • ES6 class 通常有较浅的原型链(实例->类原型->父类原型->Object.prototype->null),避免过深层级。
    • 对于共享方法,考虑将它们放在第一层(类原型)上。
  2. ​优先在对象自身存储频繁访问的属性 (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;
    }
    
  3. ​谨慎使用 delete 操作符:​

    • delete obj.prop​会改变对象的 Hidden Class​​,使得引擎为删除了属性的对象创建新的 Hidden Class。
    • 频繁创建和销毁 Hidden Class 会导致性能下降(尤其是在热代码路径中)。
    • ​替代方案:​​ 将不用的属性设置为 nullundefined (或初始化为无效值),而不是删除。这​​不会改变 Hidden Class​​。避免在运行时动态添加或删除关键属性。
  4. ​缓存查找结果 (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); // 只访问自身作用域中的变量,很快
      }
      
  5. ​尽量在构造函数中初始化所有属性 (Pre-initialize All Properties in Constructor):​

    • ​一次性​​在构造函数中声明和初始化对象的所有预期属性(设为初始值如 undefined, null, 0, ''),即使这些值稍后才被设置。这有助于引擎创建出最“稳定”的 Hidden Class,避免后续属性添加导致 Hidden Class 频繁转换。
    • 不要后续通过 obj.newProp = value 在方法中随意添加全新属性。
  6. ​使用 Object.seal / Object.freeze (谨慎使用):​

    • 在对象初始化完成后使用 Object.seal(obj)Object.freeze(obj)
    • Object.seal():防止添加新属性,并将所有现有属性标记为不可配置(configurable: false)。防止属性被删除。
    • Object.freeze():在 seal 基础上,防止任何现有属性的值被修改(只读)。
    • 它们有助于​​“锁定”对象的 Hidden Class​​,因为对象结构在之后不会被更改。引擎可以进行更激进的优化。但注意这极大地限制了对象的灵活性,通常只用于完全初始化好、不再更改的小型配置对象或只读数据容器。不适合需要动态变化的大多数业务对象。
  7. ​优先使用 ES6 class 语法:​

    • 现代引擎对 ES6 class 有更好的优化支持。
    • class 强制方法定义在原型上,属性初始化在构造函数中(或使用类字段),促使更优化的对象布局。
    • 清晰的继承结构 (extends) 比手写修改 __proto__Object.setPrototypeOf 更容易被引擎理解和优化。
    • ​避免使用 Object.setPrototypeOf():​​ 它比 Object.create() 更慢,而且​​会强制改变已有对象的 Hidden Class 和相关联的内联缓存,导致严重的性能回退​​。在创建时就确定好原型链(通过 newObject.create)。
  8. ​分析和测量 (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
早期 (1995-2005)
早期 (1995-2005)
new
ES1 原型系统
ES1 原型系统
Prototype.js
Prototype.js
ES5 (2009)
ES5 (2009)
Object.create
Object.create
ES6 (2015)
ES6 (2015)
Class语法糖
Class语法糖
Reflect.construct
Reflect.construct
现代 (2020+)
现代 (2020+)
工厂函数
工厂函数
组合对象
组合对象
函数式范式
函数式范式
对象创建技术演进

new 操作符是 JavaScript 面向对象编程的基石,其背后蕴含着原型继承的精妙设计。通过本文,您已深入理解:

  1. new 操作符的四步核心流程:​​创建 → 链接 → 初始化 → 返回​
  2. ​原型链的动态绑定特性​​及其性能影响
  3. 处理构造函数的​​返回值边界条件​
  4. ES6 class 语法与构造函数的​​等价转换​
  5. 避免常见陷阱的​​防护策略​

未来发展趋势中,静态类型检查(TypeScript)、不可变数据结构和函数式编程范式正逐渐改变对象创建模式。但作为 JavaScript 核心特性,深入理解 new 操作符仍至关重要,它帮助我们构建高效、可维护的复杂应用。

扩展阅读

  1. ECMAScript 规范 - new 操作符定义
  2. MDN - new 操作符文档
  3. JavaScript 原型继承深度指南
  4. V8 引擎中的对象表示

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/913879.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/913879.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring Boot + Vue.js 全栈开发:从前后端分离到高效部署,打造你的MVP利器!

文章目录一、为何选择 Spring Boot Vue.js&#xff1f;全栈开发的“黄金搭档”&#xff01;二、项目初始化与基础架构搭建2.1 后端&#xff1a;初始化 Spring Boot 项目2.2 前端&#xff1a;初始化 Vue.js 项目2.3 核心配置&#xff1a;打通前后端通信与跨域&#xff01;后端 …

容器技术技术入门与Docker环境部署

目录 一&#xff1a;Docker 概述 1&#xff1a;什么是Docker 2:Docker 的优势 3&#xff1a;Docker的应用场景 4&#xff1a;Docker核心概念 二&#xff1a;Docker 安装 三&#xff1a;Docker 镜像操作 1&#xff1a;获取镜像 2&#xff1a;查看镜像信息 3&#xff1a…

构建高效分布式系统:bRPC组合Channels与HTTP/H2访问指南

构建高效分布式系统&#xff1a;bRPC组合Channels与HTTP/H2访问指南 引言 在现代分布式系统中&#xff0c;下游服务访问的复杂性日益增加。bRPC通过组合Channels和HTTP/H2访问优化&#xff0c;提供了解决多层级RPC调用、负载均衡和协议兼容性问题的完整方案。本文将深入解析两大…

WSL创建Ubuntu子系统与 VS code 开发

文章目录一、打开Windows的虚拟化基础功能二、安装WSL和Ubuntu1. 安装 WSL2. 安装 Ubuntu三、 VScode一、打开Windows的虚拟化基础功能 控制面板-程序和功能-启动或关闭Windows功能&#xff0c;勾选适用于Linux的Windows子系统、虚拟机平台&#xff0c; 完成后根据提示重启电脑…

AlpineLinux二进制文件部署prometheus

在Alpine Linux上通过二进制文件部署Prometheus的步骤如下: 创建用户和组: groupadd prometheus useradd -g prometheus -m -s /sbin/nologin prometheus下载Prometheus二进制文件: 你可以从Prometheus的官方GitHub发布页面下载最新的二进制文件。例如,使用wget命令: wget…

IoT 小程序:如何破解设备互联的碎片化困局?

一、IoT 设备管理为何需要轻量化解决方案&#xff1f;随着物联网设备规模爆发式增长 —— 预计 2025 年全球连接数将达 270 亿台&#xff0c;传统 Native 应用开发模式的弊端日益凸显&#xff1a;某智能家居厂商开发 3 款主流设备 APP&#xff0c;需维护 iOS/Android/ 小程序 3…

Word 怎么让字变大、变粗、换颜色?

这是Word中最常用也最基础的操作之一。学会它&#xff0c;你的文档就会立刻变得重点突出&#xff0c;清晰易读。 记住一个核心前提&#xff1a;无论做什么格式修改&#xff0c;第一步永远是【先选中你要修改的文字】。 你可以把鼠标放在文字的开头&#xff0c;按住左键&#xf…

Ruby 安装 - Linux

Ruby 安装 - Linux 引言 Ruby 是一种广泛使用的高级编程语言,以其简洁、优雅和强大的功能而闻名。在 Linux 系统上安装 Ruby 是许多开发者的首要任务。本文将详细介绍如何在 Linux 系统上安装 Ruby,包括准备工作、安装过程和常见问题解决。 准备工作 在开始安装 Ruby 之前…

数组的应用示例

任意输入【0,9】范围内的整数&#xff0c;统计输入的每一种数字的个数并输出&#xff0c;输入-1结束程序 #include <stdio.h> int main(){const int number 10;int x;int i;int count[number];for ( i 0; i < number; i){count[i] 0;}printf("请输入0&#xf…

鸿蒙智行6月交付新车52747辆 单日交付量3651辆

近日&#xff0c;鸿蒙智行公布最新销量数据&#xff0c;6月单月全系交付52747辆&#xff0c;单日交付量3651辆&#xff0c;分别刷新鸿蒙智行单月、单日销量历史新高。仅用39个月实现全系累计交付80万辆&#xff0c;创下新势力汽车最快交付纪录。 尊界S800自5月30日上市以来&…

基于模糊控制及BP神经网络开关磁阻电机的matlab仿真

1.模型简介本仿真模型基于MATLAB/Simulink&#xff08;版本MATLAB 2015Rb&#xff09;软件。2.仿真算法:1&#xff09;采用转速、转矩双闭环控制算法&#xff1b;2&#xff09;外环是速度环&#xff0c;采用改进复合模糊控制&#xff0c;实现速度跟踪&#xff1b;3&#xff09;…

最新团购源码商城 虚拟商城系统源码 全开源

内容目录一、详细介绍二、效果展示1.部分代码2.效果图展示三、学习资料下载一、详细介绍 最新团购源码商城 虚拟商城系统源码 全开源 基于PHP开发的多功能在线商城系统&#xff0c;适合个人、小型企业或创业团队快速搭建自己的商品销售平台。系统界面美观&#xff0c;功能丰富…

Visual Studio 旧版软件下载教程

一、前言最近在开发过程中编译使用Cuda的版本较低&#xff0c;导致与最新的Visual Studio Community 2022 17.14.8不兼容。编译报错如下&#xff1a;[cmake] C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\include\yvals_core.h(902): e…

乐橙亮相2025广州建博会:用AI重新定义家庭安全与技术边界

在智能锁迈入AI新时代的关键节点&#xff0c;谁才是真正的技术引领者&#xff1f;2025年广州建博会&#xff0c;乐橙用一场“不炫技、重本质”的深度展演给出了答案。智哪儿在现场了解到&#xff0c;在A区3.1-28展位&#xff0c;乐橙围绕“智启新境 All in Intelligent”这一主…

快速搭建服务器,fetch请求从服务器获取数据

1.strapi首先strapi是一个api管理系统&#xff0c;可以让我们直接用网页的形式去定义自己的api&#xff0c;包括设置模型和权限等功能。首先直接在项目目录里面安装库。npx create-strapilatest server --quickstart这样就可以直接在项目目录创建一个连接数据库的服务器了。不用…

UGF开发记录_3_使用Python一键转换Excle表格为Txt文本

使用UnityGameFramework日常记录_3_配一键转换配置表 该系列只做记录 不做教程 所以文章简洁直接 会列出碰到的问题和解决方案 只适合UGF萌新 为了提高效率&#xff0c;我使用Python编写了一个脚本&#xff0c;实现了一键将Excel表格批量转换为带分隔符的Txt文件&#xff0c…

leetcode 3440. 重新安排会议得到最多空余时间 II 中等

给你一个整数 eventTime 表示一个活动的总时长&#xff0c;这个活动开始于 t 0 &#xff0c;结束于 t eventTime 。同时给你两个长度为 n 的整数数组 startTime 和 endTime 。它们表示这次活动中 n 个时间 没有重叠 的会议&#xff0c;其中第 i 个会议的时间为 [startTime[i]…

大型语言模型(LLM)的最新研究进展及相关新信息技术

大型语言模型(LLM)的最新研究进展及相关新信息技术 一、Google的Gemini 2.0系列 1. Gemini 2.0 Flash Thinking 核心技术:引入“推理时计算”(Inference-Time Computation)机制,支持模型在回答复杂问题前自主“思考”,显著提升数学和代码任务的准确性。多模态能力:支…

c++-友元函数和友元类

友元友元提供了一种突破封装的方式&#xff0c;有时提供了便利。但是友元会增加耦合度&#xff0c;破坏了封装&#xff0c;所以 友元不宜多用。 友元分为&#xff1a;友元函数和友元类友元函数问题现在尝试去在Date类里重载operator<<。无论怎样设置参数&#xff0c;只要…

alpinelinux的网络配置

在 Alpine Linux 中配置网络&#xff0c;您可以根据以下步骤进行&#xff1a; 配置本机 hostname&#xff1a; 本机hostname保存在/etc/hostname文件中。 echo alpine-web > /etc/hostname hostname -F /etc/hostname # 立即生效运行结果&#xff1a; localhost:~# echo &qu…