1.首先整理一下大致的实现流程
首先要理解call、apply、bind的最终目的是改变this的指向, 而this的指向不去复杂的的理解,可简单理解为this指向调用函数的对象,当我们直接调用一个全局定义的或对象内定义的方法时,其实函数内部this是指向window或我们定义方法的那个对象,例如在全局作用域下定义let showtime = function() {} ,此方法在声明时,已经被挂在了window下,调用此方法的本质是window.showTime(),所以此函数内部this指向window,那么实现call、apply、bind等方法,我们只需要在方法内将this指向我们传入的context即可,如果传入的context不存在, 就默认让this指向window, 思路大致是这样, 下面我们就去动手实现。
2.方法的简单实现
首先我们需要将call、apply、bind先绑定到Function的原型上, 这样每一个函数都能调用这三个方法(因为对象本身没有的属性和方法,回去原型链中查找)
Function.prototype.selfCall = function(context) {
//做些事
console.log(this)
}
定义一个方法先检验一下我们的selfcall方法, 看一下我们selfcall中的this是不是指向调用selfCall的函数
let getTime = function() {
console.log(1)
}
输出结果ƒ () { console.log(1) }, 所以与我们前面提到的一样,this指向了调用selfCall的对象,即getTime,那么我们在selfCall方法中的this已经明确, 就是 我们想要执行的函数, 即例如我们的示例中是getTime
接下来我们缓存一下传入的context, 如果context有值就用传入的值, 如果无值就设置为window
context = context || window;
context.fn = this 因为this是我们需要的函数, 我们把此函数挂在context的属性下, 当调用函数时用context.fn(), 那么我们想调用的函数内部的this就已经转变为我们传入的context了, 下面只需要将传入的参数传给函数即可, 下面是call方法的所有实现代码
Function.prototype.selfCall = function(context) {
// 为防止不是函数调用增加一个类型判断, 只有是函数才能调用, 否则抛出错误
if ( typeof this !== "function") {
throw new TypeError('not function');
}
context = context || window;
const fn = Symbol("fn");
context[fn] = this;
let arg = [...arguments].slice(1); // 因为selfCall传递的第一个参数肯定是context, 所以把剩下的参数传给函数即可
let res = context[fn](arg);
delete context[fn];
return res // 传递出res原因是将结果传递出去
}
apply 实现和call并无太大差别, 只是传参方式进行相应的改变
Function.prototype.selfApply = function(context) {
// 为防止不是函数调用增加一个类型判断, 只有是函数才能调用, 否则抛出错误
if ( typeof this !== "function") {
throw new TypeError('not function');
}
context = context || window;
const fn = Symbol("fn");
context[fn] = this;
let res;
if (arguments[1]) { /// 因为apply 传参是一个context 和一个数组, 所以通过arguments 取出数组传给函数即可,没有就不传参数
res = context[fn](...arguments[1]);
} else {
res = context[fn]();
}
delete context[fn];
return res // 传递出res原因是将结果传递出去
}
bind绑定上下文环境和call, apply稍微有些不同,因为bind只绑定上线文环境并不立即执行,所以bind中需要一个闭包来完成只绑定context不执行的函数
Function.prototype.selfBind = function(context) {
// 为防止不是函数调用增加一个类型判断, 只有是函数才能调用, 否则抛出错误
if ( typeof this !== "function") {
throw new TypeError('not function');
}
let _this = this; // 缓存this对象在闭包中使用
let arg = [...arguments].slice(1); // 取出context的参数
return function F() {
if ( this instanceof F) {// 有人可能会用new调用, 就需要此步骤来处理
return _this(...arg, ...arguments);
} else {
// 此处用我们自己实现的myApply来处理,并将selfBind中的参数和F中的参数合并传递给要执行的函数
return _this.myApply(context, ag.concat(...arguments));
}
}
}