call、apply了解一下

对javascript的call()和apply()这两个方法可以说是既熟悉又陌生,熟悉的是经常会在面试或者平时工作中遇到,陌生的是对它俩一直都是一知半解。刚好最近工作不是太忙,抽出时间来学习一下这两个方法。

1、定义

在javascript中,call和apply都是为了改变某个函数运行时的上下文(context)而存在的,换句话说,就是为了改变函数体内部this的指向。

  function cat(){}
    cat.prototype = {
        food:'fish',
        eat:function(){
            console.log('i like to eat '+ this.food)
        }
    }
    var whiteCat = new cat;
    whiteCat.eat();

现在我们又有一个对象dog = {food:‘bone’},我不想对它重新定义eat方法,这时我们就可以通过call或apply来调用whiteCat的eat方法。

var dog = {food:'bone'}
whiteCat.eat.call(dog)  //i like to eat bone
whiteCat.eat.apply(dog)  //i like to eat bone

所以从上面的结果我们就可以总结出:当某一个对象没有某个方法(dog没有eat方法),但是其他对象有,此时我们就可以通过call或apply用其他对象的方法来操作。

2、用法

call和apply的调用基本一样,唯一不同的是传参。

function.call(thisArg, arg1, arg2, ...)

thisArg:可选的,在function函数运行时使用的this值。
arg1,arg2,…:指定的参数列表

func.apply(thisArg, [argsArray])

thisArg:必选的,在func函数运行时使用的this值。
argsArray:可选的,一个数组或类数组对象,其中的数组元素将作为单独的参数传给func函数。

apply()的其他用法

apply有一个巧妙的用处,就是可以将一个数组默认的转换为一个参数列表([param1,param2,param3]转换为param1,param2,param3),借助这个特性,可以在某些情况下提高开发效率:

1)Math.max可以实现得到数组中最大的一项

 var arr = [1,2,3,4];
 var max = Math.max.apply(this,arr)
 console.log(max);  // 4

因为Math.max参数里面不支持Math.max([param1,param2]),也就是不支持数组,但是它支持Math.max([param1,param2),所以可以根据apply的那个特点来解决。

2)Math.min可以实现得到数组中最小的一项

和Math.max一样

var arr = [1,2,3,4];
var min = Math.min.apply(this,arr)
console.log(min);  // 1

3)Array.prototype.push可以实现两个数组合并

数组的push方法不能push一个数组,但是它可以push(param1,param2),所以我们可以通过apply来转换一下这个数组

var arr1 = [1,1,1]
var arr2 = [2,2,2]
Array.prototype.push.apply(arr1,arr2);
console.log(arr1);  //[1, 1, 1, 2, 2, 2]

3、实现函数call和apply

随着前端技术的不断更新,市场对前端开发人员的要求也越来越高。在很多面试中,面试官不仅要求你会使用call和apply,还会要求你自己实现一个call或者apply函数。在前面我们讲了call和apply的应用场景:就是一个对象没有某个方法,我们就借用有这个方法的其他对象来调用此方法达到我们的目的。跟据这样的需求,我们实现函数的步骤就出来了:

  1. 将我们需要用到的函数设为对象的属性
  2. 执行该函数
  3. 删除该函数
Function.prototype.selfCall = function (context) {
    //参数可以传null,当为null的时候,视为指向window
    var context = context || window;
    context.fn = this;
    var args = [];
    //call可以传参,并且参数不确定  我们就从Arguments对象中取值,
    //注意是从第二个开始取值的
    for(var i = 1, len = arguments.length; i < len; i++) {
        args.push('arguments[' + i + ']');
    }
    //在这里我们使用了eval函数,eval()可计算某个字符串,并执行其中的javascript代码
    //当然可以用es6的方法,之所以用eval()是因为call就是es3的方法
    var result = eval('context.fn(' + args +')');
    //删除添加的函数
    delete context.fn
    return result;
}

代码不是太难,上面都有注释,如果哪一步不太懂,自己打断点走一遍应该就会明白了,也可以点击这里,一位大佬讲的,非常详细。
apply的实现和call类似,就直接贴代码了哈

Function.prototype.selfApply = 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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章