call
以 call 为例子,我们来分析下实现一个 call 函数我们需要思考些什么?
首先,我们来分析下实现一个 call,我们需要实现那些功能:
- 这个方法是挂在函数原型上的;
- 这个函数接受几个参数,第一个参数是需要指向的this,后面的参数是传入函数的参数;
- 这个方法会被立即执行,并且被执行的函数有返回值,最后也会返回这个值;
- 当第一个参数为null、undefined、空时,默认为window;
- 当 this 指向为非引用对象的时候,this 会指向对应类型的实例;
call 实现
Function.prototype._call=function(){
//将类数组转换为数组
//1.Array.from
//2.const args=Array.prototype.slice.apply(arguments)
//3.for of ... for in ...
const args=Array.from(arguments)
//取出需要改变的this指向
let target=args.shift() || window
//处理this指向为非引用类型时
if(typeof target!=='object'){
target=new target.constructor()
}
//添加唯一键值
const fn=Symbol()
//给指定目标添加当前要执行的函数即this,这也是最重要的一步
target[fn]=this
const result = args ? target[fn](...args) : target[fn]()
//删除添加的属性,避免污染
delete target[fn]
//返回函数执行的结果
return result
}
apply实现
Function.prototype._apply = function (target) {
//空值处理
target = target || window;
if (typeof target !== "object") {
target = new target.constructor();
}
//创建一个唯一的键值,避免冲突
const fn = Symbol();
//将函数挂载在指定目标的 fn 上
target[fn] = this;
//提取函数参数
const args = arguments[1];
const result = args ? target[fn](...args) : target[fn]();
//删除引用
delete target[fn];
//返回函数执行的结果
return result;
};
bind 手写
Function.prototype._bind=function(){
const args=Array.from(arguments);
const fn = Symbol()
let target=args.shift() || window;
if(typeof target !== 'object'){
target=new target.constructor()
}
target[fn]=this
// const _this=this
return function (...args){
target[fn](...args)
// _this.apply(target,[...args])
delete target[fn]
}
}
总结
其实只要能实现call、bind、apply其中的任何一个,另外的2个都很好实现,只是在处理函数参数的时候有些许不同,还有要注意的点就是call、apply改变this指向会立即执行,而bind则需要再次执行,因为它返回的是一个函数。重点在于理解this的指向问题,能搞懂不同情况的this指向,那么实现这些就不是问题。