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