JavaScript模拟实现new操作符原理详解

作者:纸飞机

时间:2026-10-24

浏览:309

本文深入解析JavaScript中new关键字的工作机制,并通过分步代码演示如何手动模拟实现该功能。内容涵盖创建空对象、链接原型链、绑定this上下文以及处理构造函数返回值等核心逻辑,最终提供基于Object.create的优化实现方案。

文章目录

new操作符的基本特性分析

new是JavaScript语言中的一个关键字,主要用于作为运算符来实例化构造函数。示例如下:


function Animal(name) {
  this.name = name;
}
Animal.prototype.sayName = function() {
  console.log("I'm " + this.name);
}
var cat = new Animal('Tom');
console.log(cat.name); // Tom
console.log(cat.__proto__ === Animal.prototype); // true
cat.sayName(); // I'm Tom

通过上述示例,我们可以总结出以下两个关键结论:

  • new操作符成功实例化了一个对象;
  • 该实例对象能够访问构造函数内部定义的属性;
  • 该实例对象同样可以访问构造函数原型对象上的属性;
  • 对象的__proto__属性指向了构造函数的prototype原型;

模拟实现第一版:基础功能

由于new是语言关键字,无法直接重写,因此我们需要声明一个函数来模拟实现new的功能。首先,我们实现上述提到的三个基本特性,第一版代码实现如下:

补充说明:如果对原型和原型链的概念尚不熟悉,建议先阅读关于理解Javascript的原型和原型链的相关文章。


// construct: 构造函数
function newFunction() {
 var res = {};
 // 排除第一个构造函数参数
 var construct = Array.prototype.shift.call(arguments);
 res.__proto__ = construct.prototype;
 // 使用apply执行构造函数,将构造函数的属性挂载在res上面
 construct.apply(res, arguments);
 return res;
}

我们对上述代码进行测试:


function newFunction() {
 var res = {};
 var construct = Array.prototype.shift.call(arguments);
 res.__proto__ = construct.prototype;
 construct.apply(res, arguments);
 return res;
}
function Animal(name) {
  this.name = name;
}
Animal.prototype.sayName = function() {
  console.log("I'm " + this.name);
}
var cat = newFunction(Animal, 'Tom');
console.log(cat.name); // Tom
console.log(cat.__proto__ === Animal.prototype); // true
cat.sayName(); // I'm Tom

模拟实现第二版:处理返回值

测试结果符合预期。此时new的特性实现程度已达80%,但new操作符还有一个重要特性:


function Animal(name) {
  this.name = name;
  return {
    prop: 'test'
  };
}
var cat = new Animal('Tom');
console.log(cat.prop); // test
console.log(cat.name); // undefined
console.log(cat.__proto__ === Object.prototype); // true
console.log(cat.__proto__ === Animal.prototype); // false

如前所述,如果构造函数内部显式return了一个对象,那么new操作后的返回值将是该return的对象,而非新创建的实例。让我们实现这一特性,最终版代码如下:


// construct: 构造函数
function newFunction() {
 var res = {};
 // 排除第一个构造函数参数
 var construct = Array.prototype.shift.call(arguments);
 res.__proto__ = construct.prototype;
 // 使用apply执行构造函数,将构造函数的属性挂载在res上面
 var conRes = construct.apply(res, arguments);
 // 判断返回类型
 return conRes instanceof Object ? conRes : res;
}

再次进行测试:


function Animal(name) {
  this.name = name;
 return {
  prop: 'test'
  };
}
var cat = newFunction(Animal, 'Tom');
console.log(cat.prop); // test
console.log(cat.name); // undefined
console.log(cat.__proto__ === Object.prototype); // true
console.log(cat.__proto__ === Animal.prototype); // false

官方定义与优化实现

以上代码即为我们对new操作符模拟实现的最终版本。接下来,我们参考官方文档对new的解释:

引用MDN(Mozilla Developer Network)对new运算符的定义:

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

new操作符主要执行以下四个步骤:

  • 创建一个空的简单JavaScript对象(即{});
  • 将该对象链接到另一个对象(即设置该对象的构造函数属性);
  • 将步骤1中创建的新对象作为this的上下文;
  • 如果该函数没有返回对象,则返回this。

上述四条特性均已实现。此外,还有一种更优的实现方式,即通过Object.create方法来创建一个空对象:


// construct: 构造函数
function newFunction() {
 // 通过Object.create创建一个空对象;
 var res = Object.create(null);
 // 排除第一个构造函数参数
 var construct = Array.prototype.shift.call(arguments);
 res.__proto__ = construct.prototype;
 // 使用apply执行构造函数,将构造函数的属性挂载在res上面
 var conRes = construct.apply(res, arguments);
 // 判断返回类型
 return conRes instanceof Object ? conRes : res;
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章