or

JS中的new到底做了什么

2019年8月3日

这篇小短文里,我们来聊聊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;
}

看完上面代码中的注释,再重新思考开头的例子,是否能更加清楚些呢。


or