手写 apply 方法
1、我们要先分析 javascript 是怎么实现 apply 方法的:
function foo(age, height) {
console.log(`${this.name} is ${age}, ${height}`)
}
foo.apply({name: 'sun'}, [18, 1.88]) // sun is 18, 1.88
apply 方法是作为 foo 的一个属性直接调用的,我们可以看到,foo 本身并没有这个属性,那么 apply 是哪里来的呢?相信学习过原型和原型链后,大家都能猜到,apply 来自 Function 函数的原型中,我们可以验证一下:
console.log(Function.prototype.apply === foo.apply); // true
2、我们可以在 Function 的原型上定义一个 myApply 方法,和 apply 一样,接收两个参数,第一个是要被绑定到 this 的对象,第二个是参数数组:
function myApply() {
// 待实现的逻辑
...
}
Function.prototype.myApply = myApply;
实现方式:
function myApply(thisArg, otherArgs) {
thisArg.fn = this;
thisArg.fn(...otherArgs)
delete thisArg.fn
}
Function.prototype.myApply = myApply;
foo.myApply({name: 'sun'}, [18, 1.88])
思路:
- myApply 函数中的 this 指向调用它的函数 foo;
- 将 thisArg 绑定到 foo 的 this 上,可以通过隐式绑定实现。利用 thisArg.fn 接收 foo 函数,并通过
thisArg.fn()调用。 - 使用
thisArg.fn(...otherArgs)将参数传递给 foo 并调用。 - 删除 thisArg 上的 fn 属性,这个属性只是为了方便我们进行隐式绑定用的。
3、最后我们可以添加边界条件,防止传入错误的参数或者属性名发生冲突:
function myApply(thisArg, otherArgs) {
// 边界条件:thisArg 为null或undefined,则使用全局对象
thisArg = thisArg || window
const uniqueKey = Symbol("unique") // 防止属性名冲突
thisArg.uniqueKey = this;
thisArg.uniqueKey(...otherArgs)
delete thisArg.uniqueKey
}
Function.prototype.myApply = myApply;
foo.myApply({name: 'sun'}, [18, 1.88])
手写 call 方法
Call 方法手写就不多赘述,区别在于 call 方法传入参数不再是传入数组,所以我们采用剩余参数来接收所有参数:
function myCall(thisArg, ...otherArgs) {
// 边界条件:thisArg 为null或undefined,则使用全局对象
thisArg = thisArg || window
const uniqueKey = Symbol("unique") // 防止属性名冲突
thisArg.uniqueKey = this;
thisArg.uniqueKey(...otherArgs)
delete thisArg.uniqueKey
}
Function.prototype.myCall = myCall;
foo.myCall({name: 'sun'}, 18, 1.88) // sun is 18, 1.88
手写 bind 方法
bind 方法返回一个函数,该函数 this 永远指向 bind(obj) 传入的 obj 对象,核心实现方法如下:
// 手写 bind 方法
function myBind(thisArg, ...otherArgs) {
// 边界条件:thisArg 为null或undefined,则使用全局对象
thisArg = thisArg || window
const uniqueKey = Symbol("unique")
thisArg.uniqueKey = this;
return (newArgs) => {
thisArg.uniqueKey(...otherArgs, newArgs)
}
}
Function.prototype.myBind = myBind
const newFoo = foo.myBind({name: 'sun'}, 18)
newFoo(1.88) // sun is 18, 1.88