call
&& apply
&& bind
的作用
call
、apply
、bind
的作用都是改变函数的 this
指向
- call:
call
的第一个参数就是对函数绑定的this
,为一个对象,后面的参数是函数调用时所需要的参数 - apply:
apply
只接受两个参数,第一个与call
相同,第二个参数是为数组,内容是函数调用所需要的参数 - bind:
bind
与call
作用相同,不过bind
会返回一个新的函数,第一个参数之后的参数将绑定原函数的参数。
实现 call
先来看看原生call
的使用方法
let obj = {
a: 1
}
function fn(){
console.log(this.a)
}
fn() // undefined
fn.call(obj) // 1
call
是如何将 this
绑定到 obj
上的呢?
我们换一种思路,先看下面的代码
let obj = {
a: 1,
fn(){
console.log(this.a)
}
}
obj.fn() // 1
这段代码大家都能理解对吧,接下来,让我们打开脑洞。
如果 call
只是将调用函数绑定到了第一个参数身上呢?似乎一切就说得通了
function fn(){
console.log(this.a)
}
Function.prototype.myCall = function(context){
context = context || window
//首先判断是否传入第一个参数,如果没传,那么 this 就默认绑定到 window
context.callFn = this
let result = context.callFn()
delete context.callFn
return result
}
let obj = {
a: 1
}
fn.myCall(obj) // 1
ok,初步绑定 this
的工作我们已经完成,接下来思考如何将剩下的参数传给调用函数。
在函数中,我们不光可以访问 this
还可以访问 arguments
这个属性,他是一个类数组,内容就是传递给函数的参数,于是与,获取参数的问题也解决了。
function fn(val1,val2){
console.log(this.a)
console.log('val: ', val1, val2)
}
Function.prototype.myCall = function(context){
context = context || window
let args = []
// ES5 的方法
for(let i = 1; i<arguments.length; i++){
args.push(arguments[i])
}
/*
* ES6的方法
* args = [...arguments].slice(1)
*
* 这个做法不推荐,在实现 cal l的方法中使用 call,有点本末倒置了
* args = Array.prototype.silce.call(arguments, 1)
*/
context.callFn = this
// ES5 的做法,eval 接收一个字符串,将字符串作为 JS 代码执行
// 字符串模板会将数组变量展开
let result = eval(`context.callFn(${args})`)
/*
* ES6 的做法
* let result = context.callFn(...args)
*/
delete context.callFn
return result
}
let obj = {
a: 1
}
fn.myCall(obj, 66,11) // 1, val: 61, 11
实现 apply
基本思路跟 call
是一样的,只不过需要注意,apply
的第二个参数是数组
function fn(val,val2){
console.log(this.a)
console.log('val', val, val2)
}
Function.prototype.myApply = function(context){
context = context || window
let args = arguments[1]
context.applyFn = this
let result = eval(`context.applyFn(${args})`)
delete context.applyFn
return result
}
let obj = {
a: 1
}
fn.myApply(obj, [66,11])
实现 bind
bind
跟 call
的区别就在于 bind
会返回一个绑定 this
的新函数
function fn(val,val2){
console.log(this.a)
console.log('val', val, val2)
}
Function.prototype.myBind = function(context){
// 这里直接使用我们自己实现的 call 函数
let args = Array.prototype.slice.myCall(arguments);
// ES5 的写法
// let me = this;
// return function () {
// return me.myApply(context, args.slice(1))
// }
return () => {
// 使用已经实现的 apply 函数来绑定 this 并且 return 这个函数
this.myApply(context, args.slice(1))
}
}
let obj = {
a: 1
}
fn.myBind(obj, 66,11)