[陈同学i前端]看一看new操作符的真面目

156 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第N天,点击查看活动详情

前言

这是系列第一篇文章,希望从写下这篇文章开始,能够让自己静下心来不断夯实自身基础,写出更多的文章,做更酷的事情

相信接触过JS的同学对于new操作符都已经比较熟悉了,我们曾经写下的很多代码当中或多或少都有类似的代码片段

function SuperType(name) {
    this.name = name;
}
SuperType.prototype.getName = function () {
    return this.name;
}
const newInstance = new SuperType('陈同学i前端');

那么在这里的new操作符究竟做了些什么呢,让我们一起看看

new操作符应用

  1. 通过new操作符创建出来的SuperType实例对象(新对象)可以同时访问到构造函数this以及构造函数原型链中的属性
function SuperType(name) {
    this.name = name;
}
SuperType.prototype.getName = function () {
    return this.name;
}
const newInstance = new SuperType('陈同学i前端'); // SuperType { name: '陈同学i前端' }
const instance = SuperType('陈同学i前端'); // undefined
console.log(newInstance.name); // 陈同学i前端
console.log(newInstance.getName()); // 陈同学i前端

构造函数中的this指向new SuperType()生成的新对象

通过new SuperType()创建的每个新对象都通过[[Prototype]]关联到SuperType.protytype对象上

image-20220617155453492

但现在构造函数当中并没有返回值,而我们实际的生产开发当中根据实际情况会有对应返回值的设置

  1. 构造函数返回原始值不会影响new操作符的产生实例的结果
function SuperType(name) {
    this.name = name;
    return 100;
    // return '100';
}
SuperType.prototype.getName = function () {
    return this.name;
}
const newInstance = new SuperType('陈同学i前端'); // SuperType { name: '陈同学i前端' }
const instance = SuperType('陈同学i前端'); // 100
console.log(newInstance.name); // 陈同学i前端
console.log(newInstance.getName()); // 陈同学i前端

在构造函数中增加了原始值作为返回值,new操作符作用生产出来的实例结果并未发现改变

  1. 构造函数返回对象将会覆盖new操作符的运算结果,结果等同于直接调用函数的结果
function SuperType(name) {
    this.name = name;
    return {
        name: '陈同学i前端',
        age: 18
    };
}
SuperType.prototype.getName = function () {
    return this.name;
}
const newInstance = new SuperType('陈同学i前端'); // {name: '陈同学i前端',age: 18}
const instance = SuperType('陈同学i前端'); // {name: '陈同学i前端',age: 18}

new的实现

根据上面new的应用,我们可以对new的实现进行分析

  1. new操作符作用构造函数后会返回一下可操作对象instance
  2. 可操作对象instance约等于this,故可以访问所有挂载到this上的属性和方法
  3. 可操作对象instance可以访问到构造函数原型上的属性和方法
  4. 构造函数返回值为原始值不作处理,返回值为对象直接返回

根据以上的分析便可以进行new的代码编写

function myNew(target, ...args) {
    let obj = Object.create(target.prototype); // 创建新的对象并将该对象的原型设置为目标构造函数的原型
    let res = target.apply(obj, args); // 调用目标构造函数,将this指向新创建的对象
    return res instanceof Object ? res : obj; // 处理返回值
}

实现原理

  1. 首先myNew函数接受一定数量的参数(n>=1),第一个参数为目标构造函数,后续参数作为目标构造函数入参使用
  2. 创建一个空对象obj,由于obj对象需要访问到构造函数原型链上的属性,所以通过Object.create在创建对象时同时设置原型关系,将构造函数原型和obj联系起来
  3. 通过Function.apply将构造函数的this指向obj,并传入后续入参
  4. 判断构造函数返回值是否为对象,如果为对象则使用构造函数返回的值,否则返回obj

image-20220617155346339

小结

new操作符是前端中比较重要的知识点,其通用实现流程为:

  1. 创建一个新对象obj
  2. obj进行原型链构建,即将obj.prototype指向目标构造函数的原型对象
  3. obj绑定到目标构造函数调用的this(fn.apply(obj))构造函数在执行过程中能将this的属性同步到新对象上
  4. 通过new创建的每个对象通过[[Prototype]]链接到目标构造函数的原型对象
  5. 若目标构造函数没有返回对象类型Object则new操作符作用后会返回新的对象obj,反之则返回构造函数的返回值