在 JavaScript 中,new
操作符是用于创建一个对象实例的运算符。 使用 new
操作符调用一个构造函数,会自动执行以下四个步骤:
- 创建一个空对象。
- 将新对象的原型指向构造函数的原型。
- 执行构造函数,并将
this
指向新对象。这里会得到一个返回值,命名为result - 返回新对象(result)。
总之,我们想要实现一个new操作符,只要在函数中封装好这四步就可以了。
然而new操作符的实现有很多种方式。这里提供两种方式给大家做参考。
我们将在注释中使用(1)(2)(3)(4)来标注核心步骤。
初代目
function MyNew(func) {
let obj = {}; //(1)创建一个空对象。
if (func.prototype !== null) {
//如果原型属性不存在,则不需要设置新对象的原型为构造函数的原型
obj.__proto__ = func.prototype; //(2)设置原型
}
let result = func.apply(obj, Array.prototype.slice.call(arguments, 1));
//(3)执行构造函数并获得result
if (
typeof result === "object" ||
(typeof result === "function" && result !== null) //检查构造函数的返回值是否为对象或者函数
) {
return result; //(4)返回新对象
}
return obj; //否则返回空对象
}
arguments
对象是Javascript中的一个类数组对象,它包含了函数被调用时传入的所有参数。
Array.prototype.slice.call(arguments, 1)
的目的是将 arguments
对象(类数组对象)从第二个元素开始截取为一个真正的数组对象,并返回这个数组对象。
然而这段代码似乎有些太长了。不加注释阅读起来还是有些困难的。
Array.prototype.slice.call(arguments, 1)也有些让人不解。
我们可以将其写的更优雅一些。
二代目
function MyNew(func, ...args) {
const obj = Object.create(func.prototype);//(1)(2)创建一个空对象并将新对象的原型指向构造函数的原型
const result = func.apply(obj, args);//(3)执行构造函数并获得result
return Object(result) === result ? result : obj; //(4)返回新对象
}
怎么样,这下是不是好懂多了呢?
Object.create()
方法是 JavaScript 中用于创建一个新对象,并将其原型对象指向另一个对象或者 null
的方法。
在这里相当于帮我们把(1)(2)步都完成了。
结果测试:
function Person(name, age) { //创建一个Person构造函数
this.name = name;
this.age = age;
}
console.log(MyNew(Person, "小明", 18));
输出结果为:
最后,事实上new操作符在实际中有更多的边界处理,但这都不是我们要了解的核心。
本文用于记录自己的学习过程,欢迎大家指出错误。