apply和call都是爲了改變某個函數運行時的上下文而存在的(就是爲了改變函數內部this的指向),Function對象的方法,每一個函數都能調用;面試
使用apply或call方法,其運行的上下文指向第一個參數,apply的第二個參數是一個參數數組,call的第二個及其之後的參數都是數組裏面的元素。數組
數組之間的追加;瀏覽器
例如:多維數字轉一維app
let arr=[1,[7,8],[5,6]]; res=[].concat.apply([],arr)
擴充做用域擁有Math的min和max方法,獲取數組中的最大值和最小值;函數
let numbers = [5, 458 , 120 , -215 ];
let maxInNumbers = Math.max.apply(Math, numbers), //458工具
maxInNumbers = Math.max.call(Math,5, 458 , 120 , -215); //458
驗證是不是數組;測試
function isArray(obj){this
return Object.prototype.toString.call(obj) === '[object Array]' ;
}es5
好比: arguments對象,獲取到的文檔節點等,並無數組的那些方法:prototype
Array.prototype.slice.apply(argument);
//理論上來講這個比較快,直接在原型上查找slice方法
//但實際上比較慢
或者
[].slice.apply(arguments);
//理論上來講這個比較慢,由於要Array作一個實例化再查找slice方法
//實際上比較快,由於如今的各類自動化工具會把上一種方法轉換爲這種,而第二種代碼比較簡潔,因此會比較快;
也是改變函數體內this的指向,bind()是es5中的方法,bind會建立一個新函數,稱爲綁定函數,當調用這個函數的時候,綁定函數會以建立它時傳入bind()方法的第一個參數做爲this,傳入bind()方法的第二個及之後的參數加上綁定函數運行時自己的參數按照順序做爲原函數的參數來調用原函數;
例如:(後面的代碼皆取自張鑫旭大神的博客)
var button = document.getElementById("button"), text = document.getElementById("text"); button.onclick = function() { alert(this.id); // 彈出text }.bind(text);
但因爲ie6~ie8不支持該方法,因此若想在這幾個瀏覽器中使用,咱們就要模擬該方法,這也是面試常考的問題,模擬的代碼以下:
if (!function() {}.bind) { Function.prototype.bind = function(context) { var self = this; var args = Array.prototype.slice.call(arguments); return function() { return self.apply(context, args.slice(1)); } }; }
上面的代碼中this的指向是個容易理解錯的地方。
首先,咱們判斷是否存在bind方法,而後,若不存在,向Function對象的原型中添加自定義的bind方法。
這裏面var self = this這段代碼讓我很困擾,按理說,prototype是一個對象,對象的this應該指向對象自己,也就是prototype,但真的是這樣嗎。看看下面的代碼:
function a(){}; a.prototype.testThis = function(){console.log(a.prototype == this);}; var b = new a(); b.testThis();//false
顯然,this不指向prototype,而通過測試,它也不指向a,而指向b。因此原型中的this值就明朗了。指向調用它的對象。
Array.prototype.slice.call(arguments);
上面這段代碼,它的做用是將一個類數組轉化爲真正的數組,arguments是傳給call的那個上下文(因爲arguments本身沒有slice方法,這裏屬於借用Array原型的slice方法)。並且通過測試,若果你不給slice傳參數,那就等於傳了個0給它,結果就是返回一個和原來數組如出一轍的副本。
這以後的代碼就很好理解,返回一個函數,該函數把傳給bind的第一個參數當作執行上下文,因爲args已是一個數組,排除第一項,將以後的部分做爲第二部分參數傳給apply,前面講過apply的用法。
如此,咱們本身的這個bind函數的行爲就同es5中的bind同樣了。
總之三個的使用區別: