深入解析 JavaScript 中 new 运算符的原理与实现

作者:小括号时间:2026-10-24 18:04:35浏览:571

本文深入解析 JavaScript 中 new 运算符的底层原理。详细阐述构造函数的定义、返回值对实例创建的影响(包括返回对象与非对象的区别),以及箭头函数因缺少 prototype 属性导致无法使用 new 的原因。结合 ECMAScript 规范,梳理对象创建的五步流程,并提供一个低配版 new 的手写实现方案,帮助开发者理解实例化过程中的原型绑定与 this 指向机制。

定义

new 运算符用于创建用户自定义对象类型的实例,或具有构造函数的内置对象的实例。

通过 new [constructor] 语法可以创建对象实例,但构造函数的具体差异会导致生成的实例结果不同。

构造函数体不同

构造函数本质上也是函数,其与普通函数的唯一区别在于调用方式。任何函数只要使用 new 操作符调用,即被视为构造函数;反之,不使用 new 调用的函数则为普通函数。

因此,构造函数可以包含返回值,但这会直接影响 new 操作符返回的结果。

无返回值


function Person(name) {
  this.name = name;
}

let obj = new Person("Jalenl");
console.log(obj);

显然,打印输出为 {name:'Jalenl'}。

返回对象


function Person(age) {
  this.age = age;
  return { name: "Jalenl" };
}

let obj = new Person(18);
console.log(obj);

打印结果为 {name:'Jalenl'},这意味着 return 语句之前对实例属性的定义被完全覆盖。如果 return 返回的是一个对象,那么返回的就是该对象;那如果返回的是基本类型呢?

返回非对象


function Person(age) {
  this.age = age;
  return 1;
}

let obj = new Person(18);
console.log(obj);

代码返回 {age:21},这表明 return 语句失效了,结果等同于没有 return 语句。那如果内部没有 this 属性绑定,再返回基本数据类型会怎样?

没有属性绑定+返回非对象


function Person(){
    return 1
}
new Person()

最终返回的是一个空对象 {},这在预期之中。

综上所述,只有当构造函数通过 return 返回一个对象类型时,才能改变 new 操作符默认创建的初始结果。

构造函数类型不同

构造函数为普通函数

ECMA-262 第三版规范中描述了对象实例的创建过程:

13.2.2 [[Construct]] When the [[Construct]] property for a Function object F is called, the following steps are taken:

  1. Create a new native ECMAScript object.
  2. Set the [[Class]] property of Result(1) to "Object".
  3. Get the value of the prototype property of F.
  4. If Result(3) is an object, set the [[Prototype]] property of Result(1) to Result(3).
  5. If Result(3) is not an object, set the [[Prototype]] property of Result(1) to the original Object prototype object as described in 15.2.3.1.
  6. Invoke the [[Call]] property of F, providing Result(1) as the this value and providing the argument list passed into [[Construct]] as the argument values.
  7. If Type(Result(6)) is Object then return Result(6).
  8. Return Result(1).

总结下来就是:

  1. 在内存中创建一个新对象。
  2. 这个新对象内部的[[Prototype]]特性被赋值为构造函数的 prototype 属性。
  3. 构造函数内部的 this 被赋值为这个新对象(即 this 指向新对象)。
  4. 执行构造函数内部的代码(给新对象添加属性)。
  5. 如果构造函数返回对象,则返回该对象;否则,返回刚创建的新对象(空对象)。

第五步就已经说明了构造函数不同导致new结果不同的原因。

以下摘自MDN的解释:

当代码 new Foo(…) 执行时,会发生以下事情:

  1. 一个继承自 Foo.prototype 的新对象被创建。
  2. 使用指定的参数调用构造函数 Foo,并将 this 绑定到新创建的对象。new Foo 等同于 new Foo(),也就是没有指定参数列表,Foo 不带任何参数调用的情况。
  3. 由构造函数返回的对象就是 new 表达式的结果。如果构造函数没有显式返回一个对象,则使用步骤1创建的对象。(一般情况下,构造函数不返回值,但是用户可以选择主动返回对象,来覆盖正常的对象创建步骤)

构造函数为箭头函数

普通函数创建时,引擎会按照特定的规则为这个函数创建一个prototype属性(指向原型对象)。默认情况下,所有原型对象自动获得一个名为 constructor 的属性,指回与之关联的构造函数。


function Person(){
    this.age = 18;
}
Person.prototype
/**
{
    constructor: ƒ Foo()
    __proto__: Object
}
**/

创建箭头函数时,引擎不会为其创建prototype属性,箭头函数没有constructor供new调用,因此使用new调用箭头函数会报错!


const Person = ()=>{}
new Person()//TypeError: Foo is not a constructor

手写new

综上,熟悉了new的工作原理后,我们可以自己实现一个低配版的new,实现的关键是:

  1. 让实例可以访问到私有属性;
  2. 让实例可以访问构造函数原型(constructor.prototype)所在原型链上的属性;
  3. 构造函数返回的最后结果是引用数据类型。

function _new(constructor, ...args) {
    // 构造函数类型合法判断
    if(typeof constructor !== 'function') {
      throw new Error('constructor must be a function');
    }
    // 新建空对象实例
    let obj = new Object();
    // 将构造函数的原型绑定到新创的对象实例上
    obj.__proto__ = Object.create(constructor.prototype);
    // 调用构造函数并判断返回值
    let res = constructor.apply(obj,  args);
    let isObject = typeof res === 'object' && res !== null;
    let isFunction = typeof res === 'function';
    // 如果有返回值且返回值是对象类型,那么就将它作为返回值,否则就返回之前新建的对象
    return isObject || isFunction ? res : obj;
};

这个低配版new实现可以用来创建自定义类的实例,但不支持内置对象,毕竟new属于操作符,底层实现更加复杂。

总结

到此这篇关于JS中new的原理与实现的文章就介绍到这了,更多相关JS中new原理与实现内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!

作者最新文章