JavaScript 重学系列:深入解析 new 操作符原理与实现

作者:木棉时间:2026-10-22 21:24:05浏览:128

本文深入解析 JavaScript 中 new 操作符的四个核心执行步骤,并通过构造函数返回原始值与对象值的对比实验,阐明其返回值处理机制。在此基础上,文章展示了如何手动实现一个完整的 new 函数,涵盖原型链绑定、参数传递及返回值判断逻辑,帮助开发者夯实 JS 基础。

new 操作符的基本执行流程

new 操作符在执行时遵循四个基本步骤:

首先,创建一个全新的空对象。

其次,将构造函数的作用域绑定到该对象上,即让 this 指向这个新对象。

接着,为新对象添加构造函数中定义的基本方法和属性。

最后,返回这个新创建的对象。

上述流程对于熟悉 JavaScript 的开发者来说并不陌生。作为“JS 重学系列”的开篇之作,本文旨在进一步巩固 JavaScript 的基础知识。既然定位为“重学”,内容不会从零开始科普基础概念,若遇到不熟悉的知识点,建议读者自行查阅相关资料。

new 操作符的作用与返回值机制

new 操作符的具体作用

为了更直观地理解 new 的作用,我们先通过两个代码示例进行演示。


function Test(name) {
 this.name = name
}
Test.prototype.sayName = function () {
 console.log(this.name)
}
const t = new Test('yck')
console.log(t.name) // 'yck'
t.sayName() // 'yck'

从第一个示例中,我们可以总结出以下结论:

  • 通过构造函数 Test 使用 new 创建的实例,能够访问构造函数内部定义的属性。
  • 通过 new 操作符创建的实例,还可以访问构造函数原型链上的属性。这意味着,new 操作符在实例与构造函数之间建立了原型链连接。

然而,在当前的构造函数 Test 中并没有显式 return 任何值(默认返回 undefined)。如果构造函数显式返回了值,会发生什么情况呢?


function Test(name) {
 this.name = name
 return 1
}
const t = new Test('yck')
console.log(t.name) // 'yck'

在上述示例中,构造函数虽然返回了数字 1,但该返回值并未产生实际影响,最终得到的结果与之前的示例完全一致。

通过这个例子,我们可以得出另一个结论:

  • 如果构造函数返回的是原始值(示例中仅测试了返回 1,但尝试其他原始值结果相同),则该返回值毫无意义,会被忽略。

测试完返回原始值的情况后,我们再来看看如果构造函数返回一个对象会发生什么。


function Test(name) {
 this.name = name
 console.log(this) // Test { name: 'yck' }
 return { age: 26 }
}
const t = new Test('yck')
console.log(t) // { age: 26 }
console.log(t.name) // 'undefined'

通过这个示例可以发现,虽然构造函数内部的 this 依然正常工作,但当返回值为对象时,这个返回值会被正常返回。

基于此示例,我们再次得出一个结论:

  • 如果构造函数返回的是对象,则该返回值会被正常使用。

这两个示例揭示了一个重要原则:构造函数中应尽量避免返回值。因为返回原始值不会生效,而返回对象会导致 new 操作符的常规作用失效。

通过以上几个示例,相信大家对 new 操作符的作用已有了大致了解。接下来,我们将尝试手动实现 new 操作符。

手动实现 new 操作符

手动实现 new 操作符

首先,我们回顾一下 new 操作符的几个核心作用:

  • new 操作符最终返回一个对象,因此我们需要在内部创建一个对象。
  • 这个对象(即构造函数中的 this)可以访问挂载在 this 上的任意属性。
  • 该对象需要能够访问构造函数原型上的属性,因此必须将对象与构造函数链接起来。
  • 对于返回原始值的情况需要忽略,而对于返回对象的情况则需要正常处理。

明确了这些作用后,我们就可以着手实现具体功能了。


function create(Con, ...args) {
 let obj = {}
 Object.setPrototypeOf(obj, Con.prototype)
 let result = Con.apply(obj, args)
 return result instanceof Object ? result : obj
}

以下是一个完整的实现代码,我们通过以下几个步骤完成了它:

  • 首先,函数接受不定量的参数,其中第一个参数为构造函数,后续的参数将被传递给构造函数使用。
  • 接着,在内部创建一个空对象 obj。
  • 由于 obj 对象需要访问构造函数原型链上的属性,我们通过 setPrototypeOf 将两者关联起来。这段代码等同于 obj.__proto__ = Con.prototype。
  • 将 obj 绑定到构造函数上,并传入剩余的参数进行调用。
  • 判断构造函数的返回值是否为对象,如果是对象则使用构造函数返回的值,否则使用 obj,从而实现忽略构造函数返回的原始值。

接下来,我们使用该函数进行测试,观察其行为是否与原生 new 操作符一致。


function Test(name, age) {
 this.name = name
 this.age = age
}
Test.prototype.sayName = function () {
 console.log(this.name)
}
const a = create(Test, 'yck', 26)
console.log(a.name) // 'yck'
console.log(a.age) // 26
a.sayName() // 'yck'

尽管实现代码只有寥寥数行,但运行结果非常完美。

最后

通过本文,我们重新学习了 new 操作符。以上就是本文的全部内容,希望这些内容对大家的 JavaScript 学习或工作具有一定的参考价值。如有任何疑问,欢迎留言交流,感谢大家对本站的支持。

作者最新文章