js基础知识之call,apply,bind

96 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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
}