你真的了解new吗?

612 阅读4分钟

new做了什么?

认识new

MDN文档中给出定义:

new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。

众所周知, new 通过调用一个构造函数来创建一个实例对象,举个🌰:

// 定义一个构造函数
function Person(name, age) {
  this.name = name;
  this.age = age;
}
// new调用
const person1 = new Person("jenny", 18);
console.log(person1) // { name: "jenny", age: 18 }
console.log(person1 instanceof Person) // true

那么 person1 就是通过new运算符调用 Person构造函数 所创建出的对象,并且创建的对象类型是 Person 类型,或许你会问:什么是构造函数?和普通函数有什么区别?

认识构造函数

我们先理解什么是构造函数

  • 构造函数也称之为构造器(constructor),通常是我们在创建对象时会调用的函数;
  • 在其他面向的编程语言里面,构造函数是存在于类中的一个方法,称之为构造方法;
  • 但是JavaScript中的构造函数有点不太一样;

JavaScript中的构造函数是怎么样的

  • 构造函数也是一个普通的函数,从表现形式来说,和千千万万个普通的函数没有任何区别;
  • 如果这么一个普通的函数被使用new操作符来调用了,那么这个函数就称之为是一个构造函数;

那么被new调用有什么特殊的呢

如果一个函数被使用new运算符调用了,那么它会执行如下操作:

  1. 在内存中创建一个空的简单Javascript对象:
  • const moni = {}
  1. 为新创建的对象添加属性 __proto__ ,即 [[prototype]] 属性,并将该属性链接至构造函数的原型对象(prototype属性):
  • moni.__proto__ = Person.prototype
  • 实际开发中不要使用 __proto__ ,MDN文档明确弃用该属性
  1. 处理构造函数内部的this,使其指向创建出来的新对象
  2. 执行函数的内部代码(函数体代码):
  • fn.apply(moni)
  1. 如果构造函数没有返回非空对象,则返回创建出来的新对象。

如何实现new?

基本实现思路

那么,了解了new创建一个对象的过程中做了什么,接下来我们就可以按照这个思路动手实现它

/*
* constructorFn: 传入一个构造函数
* ...args: 传给构造函数的参数
*/
function __new(constructorFn, ...args) {
  // 1. 在内存中创建一个空的简单Javascript对象
  const resObj = {}
  // 2. 为新创建的对象添加属性__proto__,即 [[prototype]] 属性,并将该属性链接至构造函数的原型对象(prototype属性)
  resObj.__proto__ = constructorFn.prototype
  // 3. 处理构造函数内部的this,使其指向创建出来的新对象
  // 4. 执行函数的内部代码(函数体代码)
  const res = constructorFn.call(resObj, ...args)
  // 5. 如果构造函数返回非空对象,则返回这个非空对象
  if(res !== null && typeof res === 'object') {
    return res
  }
  // 6. 如果构造函数没有返回非空对象,则返回创建出来的新对象
  return resObj
}

但是前面我们提到,MDN文档明确弃用 __proto__ 属性,那我们要怎么优化呢?

认识Object.create()

首先让我们了解一下Object.create(),MDN文档中对其的定义为:

Object.create() 方法用于创建一个新对象,使用现有的对象来作为新创建对象的原型(prototype)。

用法如下: image.png 也就是说,调用 Object.create() 方法,传入构造函数的原型对象,会返回一个新对象,该对象的 __proto__ 指向传入的构造函数的原型对象

优化__new

function __new(constructorFn, ...args) {
  // 1. 调用Object.create(),创建一个指向 构造函数的原型对象 的新对象
  const obj = Object.create(constructorFn.prototype)
  // 2. 处理构造函数内部的this,使其指向创建出来的新对象
  // 3. 执行构造函数并定义一个 res 变量来接受返回值
  const res = constructorFn.call(obj, ...args)
  // 4. 如果构造函数返回非空对象,则返回这个非空对象,否则返回新创建的对象
  if(res !== null && typeof res === 'object') {
    return res
  }
  return obj
}

总结

实现__new

  1. 调用Object.create(),创建一个指向 构造函数的原型对象 的新对象
  2. 使用call()函数执行构造函数处理构造函数内部的this,使其指向创建出来的新对象,并定义 res 来接受构造函数的返回值
  3. 如果构造函数返回非空对象,则返回这个非空对象,否则返回新创建的对象
function __new(constructorFn, ...args) {
  const obj = Object.create(constructorFn.prototype)
  const res = constructorFn.call(obj, ...args)
  return res !== null && typeof res === 'object' ? res : obj
}

使用__new

function __new(constructorFn, ...args) {
  const obj = Object.create(constructorFn.prototype)
  const res = constructorFn.call(obj, ...args)
  return res !== null && typeof res === 'object' ? res : obj
}
// 定义一个构造函数
function Person(name, age) {
  this.name = name;
  this.age = age;
}
// new调用
const person1 = __new(Person, "jenny", 18);
console.log(person1) // { name: "jenny", age: 18 }
console.log(person1 instanceof Person) // true