new 操作符 深入理解发现与MDN描述有出入

173 阅读3分钟

1. 前言

最近打算充实一下自己,所以打算复习一下js基础,与之前不同的是这次复习打算把内容都整理成文章发布出来。因为我发现自己的遗忘速度越来越快了,既然脑子不好使了,就靠我的烂笔头吧。 言归正传,今天在复习new的时候发现了关于new构造函数内返回值的问题,在探讨这个问题之前,先复习一下new内部的过程,熟悉这一部分的大佬可以直接跳到3.2 关键字new的返回值

2. new的过程

参考MDN上关于new过程的描述

new关键字会进行如下的操作:

  1. 创建一个空的简单JavaScript对象(即 {} );
  2. 为步骤1新创建的对象添加属性 proto ,将该属性链接至构造函数的原型对象 ;
  3. 将步骤1新创建的对象作为this的上下文 ;
  4. 如果该函数没有返回对象,则返回this

这里的第四句中的对象我认为是非空的内容,既除去null和undefined

3. 手写new

按照上述描述我们很容易的便写出下面的代码

function selfNew(fun, ...args) {
  // 创建一个空的简单JavaScript对象(即{}
  let obj = {};
  // 为步骤1新创建的对象添加属性__proto__,将该属性链接至构造函数的原型对象
  obj.__proto__ = fun.prototype;
  // 将步骤1新创建的对象作为this的上下文
  let result = fun.apply(obj, args);
  // 如果该函数没有返回对象,则返回this(obj)
  return result === undefined || result === null ? obj : result;
}

3.1 验证代码:

// 构造函数
function Person(name) {
  this.name = name;
  return null;
  // return 1;
  // return '1';
  // return true;
  // return Symbol('1');
  // return BigInt(1);

  // return [1];
  // return { x: 1 };
  // return function () {};
}

// 正常的new
let p1 = new Person('HuLu');
console.log(p1);
console.log(p1.__proto__.constructor);

// 自己写的new
let p2 = selfNew(Person, 'HuLu');
console.log(p2);
console.log(p2.__proto__.constructor);

发现问题,当返回类型为非null和undefined的基本数据类型时,正常的new仍然返回的是“this”,而我们的new则返回了基础数据类型!

  • return 1 或 retun '1': image.png
  • return true:

image.png

3.2 关键字new的返回值

经过上述代码运行发现构造函数中return类型不同,实例对象的结果见下表。

运行环境为node@14.17.3 |构造函数中return的内容|构造函数中return的数据类型|实例对象的结果|实例对象的类型| |-|-|-|-| |undefined|undefined|Person { name: 'HuLu' }|object| |null|null|Person { name: 'HuLu' }|object| |1|number|Person { name: 'HuLu' }|object| |'1'|string|Person { name: 'HuLu' }|object| |true|boolean|Person { name: 'HuLu' }|object| |Symbol(1)|symbol|Person { name: 'HuLu' }|object| |BigInt(1)|bigInt|Person { name: 'HuLu' }|object| |{x: 1}|object|{x: 1}|object |[1]|array|[ 1 ]|array |function() {}|function|[Function (anonymous)]|function|

由此可见,当构造函数中的返回值为引用数据类型时,实例对象才是其返回值,否则为“新创建的obj”

3.3 新的手写new:

function selfNew(fun, ...args) {
  let obj = {};
  obj.__proto__ = fun.prototype;
  let result = fun.apply(obj, args);
  // null的typeof也是object
  return ['object', 'function'].includes(typeof result) && result !== null ? result : obj;
}

可以使用Object.create指定原型链

function selfNew2(fun, ...args) {
  let obj = Object.create(fun.prototype);
  let result = fun.apply(obj, args);
  return ['object', 'function'].includes(typeof result) && result !== null ? result : obj;
}

4. MDN的描述有误?

很不解为什么MDN的描述有误,查了一些资料也没找到原因,直到我翻看的英文版本的MDN

image.png 这个案例在解释new执行的时候提到由构造函数返回的对象(非null、false、3.1415或其他基本类型)成为整个新表达式的结果,而我们的中文版却没有这句翻译。。。

image.png

image.png 所以呢,自身条件允许的话,我们还是直接查阅原版英文文档吧(本人是个英语渣渣)。