JS 中的 new 到底做了什么
这篇小短文里,我们来聊聊 js 里的 new
运算符到底干了什么。
预备知识:你需要对 this
有所熟悉,需要对原型链知识有所熟悉。
开始聊前,先放热身代码,大家瞅下下面代码的运行结果会是什么呢?
function Person(name) {
this.name = name;
}
Person.prototype.say = function() {
console.log(this.name)
}
function FrozenPerson(name) {
this.name = "SnowMan"
}
FrozenPerson.prototype.shake = function() {
console.log(this.name, 'shake shake');
}
const alice = new Person("alice")
Person.prototype = FrozenPerson.prototype
const alice2 = new Person("alice2")
console.log(alice.name);
console.log(alice2.name);
try { alice.say();} catch (e) {}
try { alice2.say();} catch (e) {}
try { alice2.shake();} catch (e) {}
说出上面代码的运行内容似乎也没必要知道 new
运算符具体干了啥,仅凭借对原型链和 this
的了解就能说出来了,所以例子并不太严谨哈 😅,但如果对 new
了解,那肯定会更加自信自己的答案。
下面进行正题 😉:
new
在 js 里是一个运算符/操作符(operator),既然是运算符,自然有它的“运算规则”,那么它具体是怎么“运算”的呢?
每个运算符都有它的作用对象(operand),比如加法运算符+
的作用对象是它两边的内容,可以是数字,可以是字符串等。而 new
运算符的作用对象则必须是一个函数或一个类,这里暂不讨论类,不过类的本质还是函数,它就是一个语法糖而已。
下面来手动实现一个自己的 new
运算符从而直观理解它的“运算规则”到底是什么样的,由于 js 里没有类似 c++ 的运算符重载,所以得用函数来模拟实现,由于 new
是 js 的保留关键字,所以这里使用 myNew 作为函数名,详见下面代码:
// 这是我们的构造函数
function Person(name) {
this.name = name;
}
// 调用自定义的new运算符
const alice = myNew(Person, "alice");
// 自定义new运算符的具体实现
function myNew(constructor, ...args) {
// 第1步:建立一个空对象
const obj = {};
// 第2步:设置上面空对象的原型
Object.setPrototypeOf(obj, constructor.prototype);
// 第3步:执行(构造)函数,并绑定其this为第1步中建立的空对象,同时传入函数参数
const customObj = constructor.apply(obj, args);
// 第4.1步:如果上面(构造)函数返回的是一个对象,那么new运算会直接返回该对象
if(typeof customObj === 'object')
return customObj
// 第4.2步:如果返回的不是对象,比如是个字符串、数字、undefined等原始类型,则new运算会返回第一步中创建的那个对象
else
return obj;
}
看完上面代码中的注释,再重新思考开头的例子,是否能更加清楚些呢。