一、call & apply
我们先举个栗子看下call和apply的作用:
//全局变量name
name='window\'s name'
let obj = {
name:'XDD'
}
function fun(a,b){
console.log(this.name)
console.log(a+b)
}
fun(1,2) //此时this是window,output: window's name 3
fun.call(obj,1,2) //此时this是obj, output: XDD 3
fun.apply(obj,[1,2]) //此时this是obj, output: XDD 3
可以看出来,这两者的作用是改变调用者的this指向
,要注意的是它们的第二个参数的形式不同。
call()的
第二个参数
的形式需为参数列表
apply()的第二个参数
的形式需为数组
二、找找思路
既然知道了他两的作用,那么问题也很明了了:如何改变调用者的this指向,让其重新指向我们传入的对象?
在思考这个问题之前,咱不妨先思考一下在上面的栗子中,如果不使用call和apply函数,我们该怎么在函数fun执行时把它的this指向obj?相信很多小伙伴都能想到下面这种简单有效的方法——把fun变为obj对象的方法
,这样fun执行时,this就指向了obj。如下所示:
let obj = {
name:'XDD',
fun: function(a,b){
console.log(this.name)
console.log(a+b)
}
}
obj.fun(1,2) //output: XDD 3
所以,我们可以按照这种思路来实现我们的call函数和apply函数。
三、照葫芦画瓢
call的实现过程
Function.prototype.newCall = function(option,...args){
//如果传入的对象为null||undefined,我们就将option变成window
option = typeof option === 'object'?option:window;
//防止覆盖原有的属性,创建一个独一无二的值 —— fn。 Symbol(1)是不等于Symbol(1)的
const fn = Symbol()
//此时的this就是newCall的调用者,我们将其变成option对象的一个方法,
option[fn] = this
//执行该方法,并将传入的(...args)作为他的参数,将执行的结果保存在变量result中
let result = option[fn](...args)
//从option对象里删除掉这个方法(有“工具人”内味儿了)
delete option[fn]
return result;
}
apply的实现过程
Function.prototype.newapply = function(option,args){
option = typeof option === 'object'?option:window;
const fn = Symbol()
option[fn] = this
let result;
result = option[fn](...args)
delete option[fn]
return result;
}
call和apply函数就都完成了。这时候,可能有些小伙伴会问,明明这apply和call的代码没啥区别,为什么还要再复制粘贴一遍?难道你是老CV了?o_O??
如果你有此疑问,请先回想下第一点中提到的call和apply的区别,再看看这两个函数的第二个参数
(别问我咋知道有人这么想的T_T,自己细品噢……)
四、与bind区别
call 和 apply 还有 bind都有改变调用者的this指向的能力,但是call、apply 两者和bind的区别在于,bind函数返回的是一个函数,而call和apply返回的是执行结果。本来bind函数应该放在这里一起写的,但是因为先前已经写过了,所以在这里奉上链接手写实现bind函数,有兴趣的小伙子可以去see see。