JS -- (8) .call() 和 .apply() 的模拟实现

72 阅读2分钟

.call().apply().bind()方法其实效果是一样的,一句话来说就是在使用一个指定的 this 值和若干个指定的参数值的前提下调用某个函数或方法。

1) 三者的区别:

  • apply() 方法接收两个参数,( this值,参数数组 )
  • call() 方法接收若干个参数 , ( this值 , 参数1 , 参数2 , 参数3 .....)
  • bind() 方法接收一个参数,( this值 ),与上面两个不同的是,bind() 方法会创建一个新的函数实例,并将这个新的函数实例内部的 this 值指向 传进去参数!!

2) 其内部的进行操作大致为:

    • 将该函数设置为指定对象的属性,这样函数内部的this就会指向该对象,就实现了改变函数内部this指向的操作!!!
    • 执行该函数(注意要把 .call() 中传入的参数传给该函数
    • 删除该函数属性
    • 如果该函数有返回结果,就返回该结果
    • 如果,.call(null) ,则默认指定对象为 window!!!

3) 所以对 .call() 的模拟实现如下:

Function.prototype.myCall = function(context){
     5  var context = context? context : window;
     1  context.fn = this; // 因为该函数属性后面还会删除,所以随便起名
     2   // 把 .call() 后面的参数传入函数中执行 
       var args = [];
       for(var i = 1, len = arguments.length; i < len; i++) {
        args.push('arguments[' + i + ']');
       }
       var result = eval('context.fn(' + args +')');
       // 可以使用 ES6 的扩展运算符
       // var result = context.fn(...[].slice(1).call(arguments))
      3 delete context.fn
      4 return result
}      

4) 所以对 .apply() 的模拟实现如下:

Function.prototype.apply = function (context, arr) {
    var context = Object(context) || window;
    context.fn = this;

    var result;
    if (!arr) {
        result = context.fn();
    }
    else {
        var args = [];
        for (var i = 0, len = arr.length; i < len; i++) {
            args.push('arr[' + i + ']');
        }
        result = eval('context.fn(' + args + ')')
    }

    delete context.fn
    return result;
}
  • 因为 .call() 的参数传递是一个一个传的,而 .apply() 的参数,是以数组的形式传的,所以在模拟实现 .apply() 方法时,就可以直接接收!!!