重写call、apply、bind
在重写之前首先要了解这三个方法:
- 它们都是Function.prototype上的方法,都接收两个参数([this的指向],[传递的参数])
- 它们的共同点都是改变this的指向
- 不同点:
-
传递参数不同:apply区别于call和bind是apply的传递参数必须是数组
-
执行时间不同:bind区别于call和apply是call和apply调用不仅会改变this指向还会立即执行绑定的函数,而bind调用则只是将this改变和传递参数,并不会执行绑定函数,因为它返回的是一个新的函数,新函数的this就是bind的第一个参数。
-
具体使用:
let obj = {
name: 'zz',
age: 12
}
let sayHello = function (x, y) {
console.log('Hello ' + x + ' and ' + y)
}
// 现在想使用 obj.sayHello() 来调用sayHello
// call
sayHello.call(obj, 'Lucy', 'Mike') // Hello Lucy and Mike
// apply
sayHello.apply(obj, ['Lucy', 'Mike']) // Hello Lucy and Mike
// bind
console.log(sayHello.bind(obj, 'Lucy', 'Mike'))
// ƒ (x, y) {console.log('Hello' + x + 'and' + y)}
我们已经知道了它们的基本原理,那么现在让我们来实现以下
// call
/**
* 当然我们还要注意一点就是call,apply如不指定第一个参数
* 那么默认为window,且第一个参数不管是什么类型都会呗转换成引用类型
*/
Function.prototype._call = function (obj, ...params) {
obj == null ? (obj = window) : null
!/^(object|function)$/.test(typeof obj) ? Object(obj) : null
let key = Symbol('key')
obj[key] = this
let res = obj[key](...params)
delete obj[key]
return res
}
// bind 就是一个compose函数
Function.prototype._bind = function (obj, ...params) {
let that = this
return function (...args) {
params = params.concat(args)
return that.call(obj, ...params)
}
}
重写new方法
在来复习一下new具体做了些什么?
- 首先它创建了一个空对象
- 将空对象的__proto__指向构造函数的prototype
- 将构造函数的this指向空对象
上代码
function _new(Ctor, ...params) {
// 创建一个空对象
// let obj = {}
// 空对象原型链继承构造函数的原型
// obj.__proto__ = Ctor.prototype
let obj = Object.create(Ctor.prototype)
// 将构造函数的this指向obj
let res = Ctor.call(obj, ...params)
// 观察函数执行的返回值, 如果没有返回值或者返回的基础数据类型,
// 则返回 实例对象,引用数据类型则返回自己的数据
if (/^(object|function)$/.test(typeof res)) return res
return obj
}
来来来顺便就把Object.create()学了
Object.create = function (proto, properties = {}) {
let newObj = {}
newObj.__proto__ = proto
Object.defineProperties(newObj, properties)
return newObj
}