01."new"关键字做了什么?

158 阅读2分钟

前提知识

当我们使用new关键字去创建一个新对象时, 如果构造函数是一个函数或者对象的话, 那构造函数就会返回这个函数或对象, 下面我们来验证

  • 验证return一个对象
function Dog (name, age) {
    this.name = name
    this.age = age
    const obj = {}
    obj.name = "Husky"
    return obj
}

const dog = new Dog("Melisa", 3)
console.log(dog.name)
// Husky
console.log(dog.age)
// undefined
  • 验证return一个函数
function Dog (name, age) {
    this.name = name
    this.age = age
    const callBack = () => {
        console.log("我返回一个函数")
    }
    return callBack
}

const dog = new Dog("Melisa", 3)
console.log(dog.age)
// undefined
dog()
// 我返回一个函数

"new"关键字做了什么

  1. 创建一个的原型指向纯净的对象Obejct.create(null)
  2. 将对象原型指向函数的prototype
  3. 通过改变this直线调用构造函数fn.apply(...args)
  4. 判断函数的返回值, 如果返回了一个函数或者一个对象, 那就直接返回那个对象, 否则返回我们创建的那个纯净的对象

手写new

function myNew (fn, ...args) {
    const obj = Object.create(null)
    Object.setPrototypeOf(obj, fn.prototype)
    // 上面两行也可以简写成const obj = Object.create(fn.prototype), 效果一样
    const res = fn.apply(obj, args)
    const realType = Object.prototype.toString.call(res)
    if ((realType === '[object Object]' && res !== null)|| realType === '[object Function]') return res
    return obj
}

function Dog (name, age) {
    this.name = name
    this.age = age
}

Dog.prototype.eat = function () {
    console.log(`${this.name} is eating...`)
}

const dog = myNew(Dog, "Duoduo", 2)
console.log(dog.name)
// Duoduo
console.log(dog.age)
// 2
dog.eat()
// Duoduo is eating...

对于构造函数的返回值是函数或者对象的corner case大家可以自己验证一下

知识补充

1.为什么箭头函数不能作为构造函数?

因为箭头函数没有prototype(第二步就会出错) 验证:

const arrowFunc = () => {}
console.log(arrowFunc.prototype)
// undefined

2.如何阻止构造函数被当成普通函数调用?

function Dog (name, age) {this
    if (new.target !== Dog) {
        throw new TypeError("构造函数只能使用new关键字调用")
    }
    this.name = name
    this.age = age
}
const dog = new Dog("DuoDuo", 2)

3.箭头函数和普通函数的区别

  1. 写法不一样
  2. 箭头函数不能作为构造函数使用(因为没有prototype属性)
  3. this指向来自于执行上下文
  4. call, apply, bind无法改变箭头函数的this指向
class Animal {
    name = "Bela"
    arrowFunc = () => {
        console.log(this.name)
    }
}

const obj = {
    name: "Nuomi"
}

const dog = new Animal()
dog.arrowFunc.call(obj)
// Bela
  1. 箭头函数没有自己的arguments对象(not defined)
  2. 箭头函数不能用作Generator函数,不能使用yeild关键字(了解)