手写call 、apply 、bind方法
实现call方法思路
1.在Function原型上增加_call方法,接受两个参数。第一个参数为指定的this值,默认值为window;第二个参数使用剩余运算符展开,获取参数列表。
2.将函数this设置为context对象的属性,通过隐式绑定的方式调用函数,把context上的属性删了,并返回函数调用的返回值
这里的原理是:this指向调用它的对象,这是是context调用,所以this指向了context。
为什么要删除context上的属性。
防止这个属性对context造成影响,而且这个属性本身也没有用啦,不用留着占内存。
实现apply方法思路
1.在Function原型上增加_apply方法,接受两个参数。第一个参数为指定的this值,默认值为window;第二个参数接受一个数组,默认值为空数组
2.将函数this设置为context对象的属性,通过隐式绑定的方式调用函数,把context上的属性删了,并返回函数调用的返回值
实现bind方法思路
bind方法的实现可以参考apply。bind和apply的区别在于,bind是返回一个绑定好的函数,apply是直接调用。那么实现也很简单,就是返回一个函数,里面执行了apply的操作而已。
不过有一个需要判断的点,因为返回新的函数,要考虑到使用new去调用,并且new的优先级比较高,所以需要判断new的调用,还有一个特点就是bind调用的时候可以传参,调用之后生成的新的函数也可以传参,效果是一样的,所以这一块也要做处理。
前面已实现了apply 和 call ,那么bind 直接调用apply 或者 call 会更加优雅。
Function.prototype._call = function (context = window, ...args) {
// 判断调用对象
if (typeof this !== 'function') {
throw new TypeError('Error');
}
args = args ? args : []
//创建独一无二属性,以免覆盖原属性
const key = Symbol();
context[key] = this;
//通过隐式绑定的方式调用函数
const result = context[key](...args);
//删除添加的属性
delete context[key];
//返回函数调用的返回值
return result;
};
// 第二个参数是数组
Function.prototype._apply = function (context = window, args = []) {
// 判断调用对象
if (typeof this !== 'function') {
throw new TypeError('Error');
}
const key = Symbol();
context[key] = this;
//通过隐式绑定的方式调用函数
const result = context[key](...args);
//删除添加的属性
delete context[key];
//返回函数调用的返回值
return result;
};
Function.prototype._bind = function (context, ...args) {
if (typeof this !== 'function') {
throw new TypeError('Error');
}
//返回一个绑定this的函数,我们需要在此保存this
const fn = this;
return function newFn(...newFnArgs) {
if (this instanceof newFn) {
return new fn(...args, ...newFnArgs)
}
return fn.apply(context, [...args, ...newFnArgs])
}
};
let obj = { x: 1 };
function fn() {
console.log(this.x, arguments);
}
fn._call(obj, 1, 2, 3);
fn._apply(obj, [1, 2, 3]);
const bindFn = fn._bind(obj, 1, 2, 3);
bindFn();
均输出1 [Arguments] { '0': 1, '1': 2, '2': 3 }