JavaScript 重学系列:深入解析 new 操作符原理与实现
本文深入解析 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 学习或工作具有一定的参考价值。如有任何疑问,欢迎留言交流,感谢大家对本站的支持。
JavaScript怎么准确判断数据类型:typeof、instanceof与Object.prototype.toString
2026-11-05 17:09:21
JS中将数组和对象混合组装为JSON字符串
2026-11-05 16:41:42
JS运算符优先级与结合性的区别是什么?
2026-11-05 13:22:02
JS Map怎么只遍历Key或Value
2026-11-05 13:00:36
D3.js 与 ECharts 选型对比:灵活性 vs 开发效率
2026-11-05 09:45:01
