在Javascript中,每一個函數都包含兩個非繼承而來的方法,call
和apply
。這兩個方法的用途都是在特定的做用域中調用函數,實際上等於設置函數體內的this
對象的值。
摘自《JavaScript高級程序設計》
apply
方法接收兩個參數,第一個參數是在其中運行函數的做用域,第二個是一個參數數組或者arguments對象。call
方法與apply
方法做用相同,第一個參數也相同,區別在於,其他的參數須要逐個列出。javascript
apply(thisArg, argArray); call(thisArg[,arg1,arg2…]);
是使用call
仍是apply
要看具體的狀況。若是你知道全部參數或者參數的數量很少,能夠使用call
;若是參數的數量不肯定,或者數量很大,或者你收到的是一個數組或者是個arguments對象,則須要使用apply
。java
下面是使用apply的一些典型例子編程
// 得到數組中最大的元素 var arr = [1, 8, 10, 3, 24, 89, 26]; var m = Math.max.apply(Math, arr); // m => 89 // 將類數組的對象轉爲數組 var arr = Array.prototype.slice.call(arguments);
事實上,call
和apply
真正的用武之地在於,他們可以擴充函數賴以運行的做用域。數組
咱們再來看下面的例子瀏覽器
var name = 'out' var o1 = { name: 'hello' }; var o2 = { name: 'world' }; function sayName() { alert(this.name); } sayName.call(this); // out sayName.call(window); // out sayName.call(o1); // hello sayName.call(o2); // world
前兩個輸出相同,由於在全局做用域,this即爲window(瀏覽器環境)。
剩下的兩個,咱們分別改變了他們的執行環境,分別指向了o1和o2,因而結果就是顯示對象各自的name值。app
那麼,使用call和apply有什麼好處呢?咱們發現,一樣的一個函數,當指定不一樣的執行環境時,會產生不一樣的結果,這麼作的一個最大的好處就是解耦。模塊化
使用call和apply,函數和對象沒有強依賴關係,多個對象能夠使用同一個函數,避免了資源的浪費,同時對於模塊化編程也大有幫助。函數
若是你仍然對call和apply沒有清晰的認識,能夠試着這樣理解。
咱們把方法比做是工具,好比一把刀;而變量是具體的實物,好比一個蘋果。咱們能夠使用這把刀切不少不一樣的蘋果,在切蘋果的過程當中,實際上就是改變了刀的做用對象---不一樣的蘋果。工具
在上面的例子post
// 將類數組的對象轉爲數組 var arr = Array.prototype.slice.call(arguments);
slice
是一個方法,可是它是屬於Array對象prototype屬性全部的,在對arguments使用時,咱們能夠理解爲借用。
好比張三會砍樹,即張三有砍樹
這個方法(至於張三有沒有樹無所謂),而李四有樹,但他卻不會砍,這時李四即可以借用張三砍樹的方法來砍本身的樹,寫成代碼就是
var zhangsan = { cut: function() { alert(this.tree); } }; var lisi = { tree: '楊樹' }; zhangsan.cut.call(lisi); // alert('楊樹')
還有一種狀況是,李四本身也會砍樹,可是有一天他病了,砍不動了,這時他也能夠借用張三的砍樹方法砍本身的樹。在代碼中就是
String.prototype.toString = function() { return 'shit'; // 全部的String的實例對象的toString方法都被污染了,只會輸出'shit' }; var str = 'hello'; console.log(str.toString()); // 'shit' // 這時候怎麼辦呢? // 咱們須要找一個沒被污染的toString方法借來用一用,好比Object(或者Array等除了String的均可以) console.log(Object.prototype.toString.call(str)); // 輸出正常的'[object String]'
到了這裏,相信你對call和apply已經有了一個比較形象的認識了。之後再遇到相似的問題時,不妨想象成現實中的關係,可能困擾許久的問題就豁然開朗了。
call和apply另一個應用就是函數的柯里化和反柯里化技術,有興趣的能夠看下面兩篇文章
Javascript中有趣的反柯里化技術
由JavaScript反柯里化所想到的