假设有如下的 add 方法:
function add(num1, num2) {
return num1 + num2
}
手写call:
// call 方法调用场景:console.log(add.call(null, 1, 2))
Function.prototype.myCall = function (context) {
if(typeof this != 'function'){
throw new TypeError('Not a function')
}
// 如果 context 没有传的话,默认指向 window
context = context || window
// 将执行 myCall 的函数赋值到 context 对象中,
// 这样,当通过 context 执行 fn 函数的时候,fn 中的 this 会指向 context
context.fn = this
// 获取执行 fn 函数时的参数,从 1 下标开始截取
let args = Array.from(arguments).slice(1)
// 执行 fn 函数获取结果
let result = context.fn(...args)
// 删除 context 中的 fn,
// 因为原生的 call 方法并不会向 context 中添加额外的属性,我们需要与其报持一直
delete context.fn
// 返回计算的 result
return result
}
手写apply:
// apply 方法调用场景:console.log(add.apply(null, [1, 2]))
Function.prototype.myApply = function (context) {
if(typeof this != 'function'){
throw new TypeError('Not a function')
}
// 如果 context 没有传的话,默认指向 window
context = context || window
// 将执行 myApply 的函数赋值到 context 对象中,
// 这样,当通过 context 执行 fn 函数的时候,fn 中的 this 会指向 context
context.fn = this
// 执行 fn 函数,apply 的用法是第二个参数为函数参数的数组,用户可能传递了这个参数数组,也有可能没有传递,所以这里用 (arguments[1] || []) 这种兼容写法
let result = context.fn(...(arguments[1] || []));
// 删除 context 中的 fn,
// 因为原生的 apply 方法并不会向 context 中添加额外的属性,我们需要与其报持一直
delete context.fn
// 返回计算的 result
return result
}
手写bind:
// bind 方法调用场景:console.log(add.bind(null, 1, 2)())
Function.prototype.myBind = function (context) {
// bind 方法的返回值是一个函数,这一点和 call、apply 是不一样的
if(typeof this != 'function'){
throw new TypeError('Not a function')
}
// 保存执行 myBind 方法的函数
let _this = this
// 获取执行 myBind 时传递的参数
let args = Array.from(arguments).slice(1)
// 返回一个函数
return function F() {
// 返回的函数有两种用法。
// 第一种用法:new F(xxx)
// 第二种用法:直接嗲用 let result = F(xxx)
// 下面使用 instanceof 判断是上面两种用法的哪一种
// 因为 bind 有这样的特性:Foo.bind(context, 1)(2) 等价于 context.Foo(1, 2)
// 所以我们需要把这两处参数合在一起
if(this instanceof F){
return new _this(...args, ...arguments)
} else {
return _this.apply(context, args.concat(...arguments))
}
}
}