對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的應用場景:就是一個對象沒有某個方法,我們就借用有這個方法的其他對象來調用此方法達到我們的目的。跟據這樣的需求,我們實現函數的步驟就出來了:
- 將我們需要用到的函數設爲對象的屬性
- 執行該函數
- 刪除該函數
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;
}