在JavaScript语法中,call,apply和bind可以为函数显式绑定this
实际上是用C++实现的,但是我们可以用JS代码写一个自己的call,apply和bind函数,实现相同功能
一、call函数的实现
function fn(){
console.log('fn')
}
fn.call(thisArg, ...args) //第一参数为函数fn执行时绑定的this,之后参数为传入fn的参数
fn.myCall(thisArg, ...args) //自己实现call方法
1. 思考三个问题:
- 如何给函数对象绑定myCall方法
每个函数对象都继承了JS中的Function类,可以在原型上绑定方法,那么每个实例就都继承了该方法
// 将方法定义在原型上
Function.prototype.myCall(thisArg, ...args){ }
// 函数实例可以调用
fn1.myCall();
fn2.myCall();
- 如何在myCall函数内部执行fn
在函数原型上绑定方法后,任何一个函数实例都会继承到该方法。myCall函数是通过fn来调用,所以对于myCall而言,内部this隐式绑定fn,由此可以在myCall内部拿到fn
Function.prototype.myCall(thisArg, arg1, arg2, ...){
const fn = this
}
- 如何给函数fn绑定this
采用隐式绑定的方法,将fn作为thisArg的方法来调用
thisArg.fn(arg1, arg2, ...)
2. myCall函数实现
Function.prototype.myCall(thisArg, ...args) {
const fn = this; // 获取调用myCall的函数
thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window; // Object(thisArg)即使thisArg为字符串或者数字或者布尔类型,依然将其转换为对象
const symFn = Symbol('fn'); // 将fn作为thisArg的方法调用,为了防止thisArg本身有同名方法,采用ES6的Symbol类型定义属性
thisArg[symFn] = fn;
const results = thisArg[symFn](...args);
delete thisArgs[symFn]; // 调用完之后删除属性
return results;
}
二、apply函数的实现
apply函数的用法:fn.apply(thisArg, [arg1, arg2, arg3,...])
与call函数的区别在于参数是以数组形式传递的
Function.prototype.myApply(thisArg, args) {
const fn = this;
thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window;
const symFn = Symbol('fn');
thisArg[symFn] = fn;
args = args || []; // 注意:若没传参数, args是undefined,那么...args会报错,因为undefined不具有迭代器,无法展开
const results = thisArg[symFn](...args);
delete thisArgs[symFn];
return results;
}
三、bind函数的实现
bind函数用法:
const foo = fn.bind(thisArg, ...bindArgs);
foo(...restArgs);
call和apply是在内部执行了函数,而bind不是,bind是返回一个函数,之后再执行
思考一个问题:
- 如何保证foo函数执行时,fn是绑定thisArg的
foo执行,内部fn依然是作为thisArg的方法来调用的就可以
bind函数实现
Function.prototype.myApply(thisArg, ...bindArgs) {
const fn = this;
thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window;
function foo(...restArgs){
const symFn = Symbol('fn');
thisArg[symFn] = fn;
const results = thisArg[symFn](...bindArgs, ...restArgs);
delete thisArgs[symFn];
}
return foo;
}