持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第18天,点击查看活动详情
call,apply,bind,一般都是拿来干啥的呢,我们在什么地方能够用到它呢。
我们在写代码的过程中经常会有一些对象不具有我们需要用的方法,但是我们又想让这个对象使用这个方法,那怎么办呢,我们就可以通过call,apply,bind这三个函数来达成这个目的
call
在js的代码中,我们经常会看见xx.call(bb,dd,ee)这样的写法,他和其他两种的区别在哪呢
- call和bind不一样的是call是立即执行函数并有返回值
- call和apply传递的参数不一样,call是可以传递多个参数,而apply只能传递一个可以是数组的参数
如何实现一个call函数呢,我们来理清一下call函数做了些什么
- 首先是获取调用函数,也就是函数中的this并将其赋给第一个参数
- 接收传递来的参数
- 调用this函数获取结果并在参数上删除函数
- 返回结果
Function.prototype.call2 = function(context){
//如果传递的参数为null的话就指向window
context = context||window
context._fn = this
let arr = []
for(let i = 1;i<arguments.length;i++){
arr.push(arguments[i])
}
let result = context._fn(...arr)
// 或者使用eval执行
持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第17天,[点击查看活动详情](https://juejin.cn/post/7099702781094674468 "https://juejin.cn/post/7099702781094674468")
\ // let result = eval('context._fn('+arr+')')
delete context._fn
return result
}
apply
apply大致上和call是差不多的,只不过呢,传递的参数只有一个。
Function.prototype.call2 = function(context,args){
//如果传递的参数为null的话就指向window
context = context||window
context._fn = this
let arr = []
let result = null
if(!args){
result = context._fn()
}else{
for(let i = 0;i<args.length;i++){
arr.push('args[' + i + ']');
}
result = eval('context._fn('+arr+')')
}
delete context._fn
return result
}
bind
bind函数和另外两个最大的不同就是,bind函数不是立即执行,而是返回一个新的函数
初步实现一下
Function.prototype.bind2 = function(context){
let self = this
let args = Array.prototype.slice.call(arguments,1)
return function (){
return self.apply(context,args.concat(Array.prototype.slice.call(arguments)))
}
}
我们实现了传递参数以及返回新函数的目标,但是bind返回的函数在用作构造函数的时候,会导致bind时期绑定的this失效,这需要我们来做一些处理。
Function.prototype.bind2 = function(context){
let self = this
let args = Array.prototype.slice.call(arguments,1)
let bound = function (){
return self.apply(this instanceof bound?this:context,args.concat(Array.prototype.slice.call(arguments)))
}
bound.prototype = this.prototype
return bound
}
我们还可以在此基础之上进行一个优化,因为此时修改新函数的prototype会影响到原函数的prototype,那我们可以通过一个空函数来中转一下解决这个问题。
Function.prototype.bind2 = function(context){
let self = this
let args = Array.prototype.slice.call(arguments,1)
let ff = function(){}
let bound = function (){
return self.apply(this instanceof ff?this:context,args.concat(Array.prototype.slice.call(arguments)))
}
ff.protytype = this.prototype
bound.prototype = new ff()
return bound
}