在调用 new 的过程中会发生以下四件事情:
1.新生成了一个对象
2.链接到原型
3.绑定 this
4.返回新对象
function create() {
let obj = {} // 创建一个空对象
let Con = [].shift.call(arguments) // 获取构造函数
obj.__proto__ = Con.prototype // 设置空对象的原型
let result = Con.apply(obj, arguments) // 绑定 this 并执行构造函数
return result instanceof Object ? result : obj // 确保返回值为对象
}
function Dog(name) {
this.name = name
this.say = function () {
console.log('name = ' + this.name)
}
}
const dog = create(Dog, 'aaa')
dog.say() // name = aaa
console.log(dog instanceof Dog) // true
补充 [].shift.call( arguments ) 和 [].slice.call( arguments )
1. [].shift.call( arguments )
[].slice.call( arguments )
// 等效于
Array.prototype.slice.call( arguments )
- 要把 arguments 转为数组对象。
本着 能少写就少写,能不写就不写的想法,想到 slice(): 可从已有的数组中返回选定的元素。
slice 不会改变原来的数组,而是返回一个子数组。
而 arguments 不是数组对象,不能调用数组的方法。
但是能少写就少写,能不写就不写 - 这时候又要解锁一个 call 函数,或者 apply 函数。这两个函数都可以改变函数 this 的指向,函数运行时的作用域。
区别:就是参数不一样,第1个参数都是一个对象或者 ‘this’ 注意 this 加引号了, apply 第 2 个参数接收一个数组,call 则不是,call可以有n个参数有多少放多少就行。 -
slice 方法原理:根据传入的参数(值)对原数组(或者类数组)进行遍历获取,赋给新数组然后返回。如果没有参数便复制整个原数组(或者类数组),后赋给新数组然后返回
重点来了
因为 slice 内部实现是使用的 this 代表调用对象。那么当** [].slice.call() ** 传入 arguments 对象的时候,通过 call 函数改变原来 slice 方法的 this 指向, 使其指向arguments,并对 arguments 进行复制操作,而后返回一个新数组。至此便是完成了arguments 类数组 转为 数组 的目的。
2. [].shift.call( arguments )
[].shift.call( arguments )
// 等效于
Array.prototype.shift.call( arguments )
shift() 方法删除数组第一项,并返回删除项。
根据上边的理解,这句代码意思就是: “删除并拿到 arguments 的第一项”