JavaScript 中經過call或者apply用來代替另外一個對象調用一個方法,將一個函數的對象上下文從初始的上下文改變爲由 thisObj 指定的新對象。簡單的說就是改變函數執行的上下文,這是最基本的用法。兩個方法基本區別在於傳參不一樣。javascript
一、語法
先來看看JS手冊中對call的解釋:html
call 方法
調用一個對象的一個方法,以另外一個對象替換當前對象。java
call([thisObj[,arg1[, arg2[, [,.argN]]]]])
參數web
thisObj可選項。將被用做當前對象的對象。 arg1, arg2, , arg可選項。將被傳遞方法參數序列。
說明
call 方法能夠用來代替另外一個對象調用一個方法。call 方法可將一個函數的對象上下文從初始的上下文改變爲由 thisObj 指定的新對象。ajax
若是沒有提供 thisObj 參數,那麼 Global 對象被用做 thisObj。
說明白一點其實就是更改對象的內部指針,即改變對象的this指向的內容。這在面向對象的js編程過程當中有時是頗有用的。編程
二、用法數組
由於function也是對象,因此每一個函數都包含兩個非繼承而來的方法:apply()和call()。這兩個方法的用途都是在特定的做用域中調用函數,實際上等於設置函數體內this 對象的值。首先,apply()方法接收兩個參數:一個是在其中運行函數的做用域,另外一個是參數數組。其中,第二個參數能夠是Array 的實例,也能夠是arguments 對象。例如:瀏覽器
function sum(num1, num2){ return num1 + num2; } function callSum1(num1, num2){ return sum.apply(this, arguments); // 傳入arguments 對象 } function callSum2(num1, num2){ return sum.apply(this, [num1, num2]); // 傳入數組 } alert(callSum1(10,10)); //20 alert(callSum2(10,10)); //20
在上面這個例子中,callSum1()在執行sum()函數時傳入了this 做爲this 值(由於是在全局做用域中調用的,因此傳入的就是window 對象)和arguments 對象。而callSum2 一樣也調用了sum()函數,但它傳入的則是this 和一個參數數組。這兩個函數都會正常執行並返回正確的結果。app
在嚴格模式下,未指定環境對象而調用函數,則this 值不會轉型爲window。除非明確把函數添加到某個對象或者調用apply()或call(),不然this 值將是undefined異步
三、不一樣點
call()方法與apply()方法的做用相同,它們的區別僅在於接收參數的方式不一樣。對於call()方法而言,第一個參數是this 值沒有變化,變化的是其他參數都直接傳遞給函數。換句話說,在使用call()方法時,傳遞給函數的參數必須逐個列舉出來,以下面的例子所示。
function sum(num1, num2){ return num1 + num2; } function callSum(num1, num2){ return sum.call(this, num1, num2); } alert(callSum(10,10)); //20
在使用call()方法的狀況下,callSum()必須明確地傳入每個參數。結果與使用apply()沒有什麼不一樣。至因而使用apply()仍是call(),徹底取決於你採起哪一種給函數傳遞參數的方式最方便。若是你打算直接傳入arguments 對象,或者包含函數中先接收到的也是一個數組,那麼使用apply()確定更方便;不然,選擇call()可能更合適。(在不給函數傳遞參數的狀況下,使用哪一個方法都無所謂) 。
四、擴充函數運行的做用域
事實上,傳遞參數並不是apply()和call()真正的用武之地;它們真正強大的地方是可以擴充函數
賴以運行的做用域。下面來看一個例子。
window.color = "red"; var o = { color: "blue" }; function sayColor(){ alert(this.color); } sayColor(); //red sayColor.call(this); //red sayColor.call(window); //red sayColor.call(o); //blue
這個例子是在前面說明this 對象的示例基礎上修改而成的。這一次,sayColor()也是做爲全局函數定義的,並且當在全局做用域中調用它時,它確實會顯示」red」——由於對this.color 的求值會轉換成window.color 的求值。而sayColor.call(this)和sayColor.call(window),則是兩種顯式地在全局做用域中調用函數的方式,結果固然都會顯示」red」。可是,當運行sayColor.call(o)時,函數的執行環境就不同了,由於此時函數體內的this 對象指向了o,因而結果顯示的是」blue」。使用call()(或apply())來擴充做用域的最大好處,就是對象不須要與方法有任何耦合關係。
在前面例子的第一個版本中,咱們是先將sayColor()函數放到了對象o 中,而後再經過o 來調用它的;而在這裏重寫的例子中,就不須要先前那個多餘的步驟了。
五、bind()方法
最後再來講 bind() 函數,上面講的不管是 call() 也好, apply() 也好,都是立馬就調用了對應的函數,而 bind() 不會, bind() 會生成一個新的函數,bind() 函數的參數跟 call() 一致,第一個參數也是綁定 this 的值,後面接受傳遞給函數的不定參數。 bind() 生成的新函數返回後,你想何時調就何時調,
window.color = "red"; var o = { color: "blue" }; function sayColor(){ alert(this.color); } var objectSayColor = sayColor.bind(o); objectSayColor(); //blue
在這裏,sayColor()調用bind()並傳入對象o,建立了objectSayColor()函數。object-SayColor()函數的this 值等於o,所以即便是在全局做用域中調用這個函數,也會看到」blue」。
支持bind()方法的瀏覽器有IE9+、Firefox 4+、Safari 5.1+、Opera 12+和Chrome。
類的繼承
先來看這個例子:
function Person(name,age){ this.name = name; this.age=age; this.alertName = function(){ alert(this.name); } this.alertAge = function(){ alert(this.age); } } function webDever(name,age,sex){ Person.call(this,name,age); this.sex=sex; this.alertSex = function(){ alert(this.sex); } } var test= new webDever(「愚人碼頭」,28,」男」); test.alertName();//愚人碼頭 test.alertAge();//28 test.alertSex();//男
這樣 webDever類就繼承Person類,Person.call(this,name,age) 的 意思就是使用 Person構造函數(也是函數)在this對象下執行,那麼 webDever就有了Person的全部屬性和方法,test對象就可以直接調用Person的方法以及屬性了
用於回調
call 和 apply在回調行數中也很是有用,不少時候咱們在開發過程當中須要對改變回調函數的執行上下文,最經常使用的好比ajax或者定時什麼的,通常狀況下,Ajax都是全局的,也就是window對象下的,來看這個例子:
function Album(id, title, owner_id) { this.id = id; this.name = title; this.owner_id = owner_id; }; Album.prototype.get_owner = function (callback) { var self = this; $.get(‘/owners/’ + this.owner_id, function (data) { callback && callback.call(self, data.name); }); }; var album = new Album(1, ‘生活’, 2); album.get_owner(function (owner) { alert(‘The album’ + this.name + ‘ belongs to ‘ + owner); });
這裏
album.get_owner(function (owner) { alert(‘The album’ + this.name + ‘ belongs to ‘ + owner); });
中的 this.name就能直接取到album對象中的name屬性了。
提及回調函數,好多人雖然知道意思,可是仍是隻知其一;不知其二。至於怎麼用,仍是有點糊塗。網上的一些相關的也沒有詳細的說一下是怎麼回事,說的比較片面。下面我只是說說我的的一點理解,大牛勿噴。
定義
回調是什麼?
看維基的 Callback_(computer_programming) 條目:
In computer programming, a callback is a reference to a piece of executable code that is passed as an argument to other code.
在JavaScript中,回調函數具體的定義爲:函數A做爲參數(函數引用)傳遞到另外一個函數B中,而且這個函數B執行函數A。咱們就說函數A叫作回調函數。若是沒有名稱(函數表達式),就叫作匿名回調函數。
舉個例子:
你有事去隔壁寢室找同窗,發現人不在,你怎麼辦呢? 方法1,每隔幾分鐘再去趟隔壁寢室,看人在不 方法2,拜託與他同寢室的人,看到他回來時叫一下你 前者是輪詢,後者是回調。 那你說,我直接在隔壁寢室等到同窗回來能夠嗎? 能夠啊,只不過這樣本來你能夠省下時間作其餘事,如今必須浪費在等待上了。 把原來的非阻塞的異步調用變成了阻塞的同步調用。 JavaScript的回調是在異步調用場景下使用的,使用回調性能好於輪詢。
所以callback 不必定用於異步,通常同步(阻塞)的場景下也常常用到回調,好比要求執行某些操做後執行回調函數。
一個同步(阻塞)中使用回調的例子,目的是在func1代碼執行完成後執行func2。
var func1=function(callback){ //do something. (callback && typeof(callback) === "function") && callback(); } func1(func2); var func2=function(){ }
異步回調的例子:
$(document).ready(callback);
$.ajax({
url: "test.html", context: document.body }).done(function() { $(this).addClass("done"); }).fail(function() { alert("error"); }).always(function() { alert("complete"); });
回調何時執行
回調函數,通常在同步情境下是最後執行的,而在異步情境下有可能不執行,由於事件沒有被觸發或者條件不知足。另外,最好保證回調存在且必須是函數引用或者函數表達式:
(callback && typeof(callback) === "function") && callback();
咱們來看一下一個粗略的一個定義「函數a有一個參數,這個參數是個函數b,當函數a執行完之後執行函數b。那麼這個過程就叫回調。」,這句話的意思是函數b以一個參數的形式傳入函數a並執行,順序是先執行a ,而後執行參數b,b就是所謂的回調函數。咱們先來看下面的例子。
function a(callback){ alert('a'); callback.call(this);//或者是 callback(), callback.apply(this),看我的喜愛 } function b(){ alert('b'); } //調用 a(b);
這樣的結果是先彈出 ‘a’,再彈出‘b’。這樣估計會有人問了「寫這樣的代碼有什麼意思呢?好像沒太大的做用呢!」
是的,其實我也以爲這樣寫沒啥意思,「若是調用一個函數就直接在函數裏面調用它不就好了」。我這只是給你們寫個小例子,作初步的理解。真正寫代碼的過程當中不多用這樣無參數的,由於在大部分場景中,咱們要傳遞參數。來個帶參數的:
function c(callback){ alert('c'); callback.call(this,'d'); } //調用 c(function(e){ alert(e); });
這個調用看起來是否是似曾相識,這裏e參數被賦值爲’d’,咱們只是簡單的賦值爲字符竄,其實也能夠賦值爲對象。Jquery裏面是否是也有個e參數?
回調函數的使用場合
資源加載:動態加載js文件後執行回調,加載iframe後執行回調,ajax操做回調,圖片加載完成執行回調,AJAX等等。
DOM事件及Node.js事件基於回調機制(Node.js回調可能會出現多層回調嵌套的問題)。
setTimeout的延遲時間爲0,這個hack常常被用到,settimeout調用的函數其實就是一個callback的體現
鏈式調用:鏈式調用的時候,在賦值器(setter)方法中(或者自己沒有返回值的方法中)很容易實現鏈式調用,而取值器(getter)相對來講很差實現鏈式調用,由於你須要取值器返回你須要的數據而不是this指針,若是要實現鏈式方法,能夠用回調函數來實現
setTimeout、setInterval的函數調用獲得其返回值。因爲兩個函數都是異步的,即:他們的調用時序和程序的主流程是相對獨立的,因此沒有辦法在主體裏面等待它們的返回值,它們被打開的時候程序也不會停下來等待,不然也就失去了setTimeout及setInterval的意義了,因此用return已經沒有意義,只能使用callback。callback的意義在於將timer執行的結果通知給代理函數進行及時處理。
當函數的實現過程很是漫長,你是選擇等待函數完成處理,仍是使用回調函數進行異步處理呢?這種狀況下,使用回調函數變得相當重要,例如:AJAX請求。如果使用回調函數進行處理,代碼就能夠繼續進行其餘任務,而無需空等。實際開發中,常常在javascript中使用異步調用,甚至在這裏強烈推薦使用!
下面有個更加全面的使用AJAX加載XML文件的示例,而且使用了call()函數,在請求對象(requested object)上下文中調用回調函數。
function fn(url, callback){ var httpRequest; //建立XHR httpRequest = window.XMLHttpRequest ? new XMLHttpRequest() : window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : undefined; //針對IE進行功能性檢測 httpRequest.onreadystatechange = function(){ if(httpRequest.readystate === 4 && httpRequest.status === 200){ //狀態判斷 callback.call(httpRequest.responseXML); } }; httpRequest.open("GET", url); httpRequest.send(); } fn("text.xml", function(){ //調用函數 console.log(this); //此語句後輸出 }); console.log("this will run before the above callback."); //此語句先輸出
咱們請求異步處理,意味着咱們開始請求時,就告訴它們完成之時調用咱們的函數。在實際狀況中,onreadystatechange事件處理程序還得考慮請求失敗的狀況,這裏咱們是假設xml文件存在而且能被瀏覽器成功加載。這個例子中,異步函數分配給了onreadystatechange事件,所以不會馬上執行。
最終,第二個console.log語句先執行,由於回調函數直到請求完成才執行。
轉載 http://blog.csdn.net/i10630226/article/details/49205355