1、call、apply的实现的最终目的是为了让函数在被绑定的对象上执行。
例如:
var obj = {a: 1};
function fn(b) {
console.log(b);
console.log(this.a)
};
obj.fn = fn;
obj.fn();
问题是这样会在obj中,添加一个多于的对象。所以我们删除它:
delete obj.fn;
call实现代码:
Function.prototype.myCall = function(context) {
// fn.myCall(obj), fn不是一个函数时,抛出异常
if (typeof this != 'function') {
throw Error('type error');
}
// arguments是一个维数组,没有slice方法
let args = Array.prototype.slice.call(arguments, 1);
// 如果fn.myCall(obj)的obj不存在,则在全局运行fn
context = context || window;
context.fn = this;
let result = context.fn(...args);
// 删除obj中的fn
delete context.fn;
return result;
}
apply实现代码:
Function.prototype.myApply = function(context) {
// fn.myApply(obj), fn不是一个函数时,抛出异常
if (typeof this != 'function') {
throw Error('type error');
}
// arguments是一个维数组,没有slice方法
let args = Array.prototype.slice.call(arguments, 1);
// apply第二参数是一个数组
args = args?.[0] ?? []
// 如果fn.myApply(obj)的obj不存在,则在全局运行fn
context = context || window;
context.fn = this;
let result = context.fn(...args);
// 删除obj中的fn
delete context.fn;
return result;
}
2、bind和apply和call有点不一样,它不会立即执行,bind会返回一个函数。
Function.prototype.myBind = function(context) {
// fn.myBind(obj), fn不是一个函数时,抛出异常
if (typeof this != 'function') {
throw Error('type error');
}
// arguments是一个维数组,没有slice方法
let args = Array.prototype.slice.call(arguments, 1);
// 保存 fn
let that = this;
// 创建一个fn
let Fn = function() {
// arguments为当前函数的的arguments
let fnArgs = Array.prototype.slice.call(arguments, 0);
// 如果 fn在this的原型链上,即this指向 new Fn()①
that.apply(this instanceof that ? this : context, [...args, ...fnArgs]);
}
// 让Fn的原型指向fn的原型,注释①的前置条件。
Fn.prototype = Object.create(that.prototype);
return Fn;
}