前提知识
当我们使用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"关键字做了什么
- 创建一个的原型指向纯净的对象
Obejct.create(null) - 将对象原型指向函数的prototype
- 通过改变this直线调用构造函数fn.apply(...args)
- 判断函数的返回值, 如果返回了一个函数或者一个对象, 那就直接返回那个对象, 否则返回我们创建的那个纯净的对象
手写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.箭头函数和普通函数的区别
- 写法不一样
- 箭头函数不能作为构造函数使用(因为没有prototype属性)
- this指向来自于执行上下文
- 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
- 箭头函数没有自己的arguments对象(not defined)
- 箭头函数不能用作Generator函数,不能使用yeild关键字(了解)