跟我學(xué)習(xí)javascript的call(),apply(),bind()與回調(diào)
一、call(),apply(),bind()方法
JavaScript 中通過(guò)call或者apply用來(lái)代替另一個(gè)對(duì)象調(diào)用一個(gè)方法,將一個(gè)函數(shù)的對(duì)象上下文從初始的上下文改變?yōu)橛?thisObj 指定的新對(duì)象。簡(jiǎn)單的說(shuō)就是改變函數(shù)執(zhí)行的上下文,這是最基本的用法。兩個(gè)方法基本區(qū)別在于傳參不同。
call(obj,arg1,arg2,arg3); call第一個(gè)參數(shù)傳對(duì)象,可以是null。參數(shù)以逗號(hào)分開(kāi)進(jìn)行傳值,參數(shù)可以是任何類型。
apply(obj,[arg1,arg2,arg3]); apply第一個(gè)參數(shù)傳對(duì)象,參數(shù)可以是數(shù)組或者arguments 對(duì)象。
1、語(yǔ)法
先來(lái)看看JS手冊(cè)中對(duì)call的解釋:
call 方法
調(diào)用一個(gè)對(duì)象的一個(gè)方法,以另一個(gè)對(duì)象替換當(dāng)前對(duì)象。
call([thisObj[,arg1[, arg2[, [,.argN]]]]])
參數(shù)
thisObj可選項(xiàng)。將被用作當(dāng)前對(duì)象的對(duì)象。
arg1, arg2, , arg可選項(xiàng)。將被傳遞方法參數(shù)序列。
說(shuō)明
call 方法可以用來(lái)代替另一個(gè)對(duì)象調(diào)用一個(gè)方法。call 方法可將一個(gè)函數(shù)的對(duì)象上下文從初始的上下文改變?yōu)橛?thisObj 指定的新對(duì)象。
如果沒(méi)有提供 thisObj 參數(shù),那么 Global 對(duì)象被用作 thisObj。
說(shuō)明白一點(diǎn)其實(shí)就是更改對(duì)象的內(nèi)部指針,即改變對(duì)象的this指向的內(nèi)容。這在面向?qū)ο蟮膉s編程過(guò)程中有時(shí)是很有用的。
2、用法
因?yàn)閒unction也是對(duì)象,所以每個(gè)函數(shù)都包含兩個(gè)非繼承而來(lái)的方法:apply()和call()。這兩個(gè)方法的用途都是在特定的作用域中調(diào)用函數(shù),實(shí)際上等于設(shè)置函數(shù)體內(nèi)this 對(duì)象的值。首先,apply()方法接收兩個(gè)參數(shù):一個(gè)是在其中運(yùn)行函數(shù)的作用域,另一個(gè)是參數(shù)數(shù)組。其中,第二個(gè)參數(shù)可以是Array 的實(shí)例,也可以是arguments 對(duì)象。例如:
function sum(num1, num2){
return num1 + num2;
}
function callSum1(num1, num2){
return sum.apply(this, arguments); // 傳入arguments 對(duì)象
}
function callSum2(num1, num2){
return sum.apply(this, [num1, num2]); // 傳入數(shù)組
}
alert(callSum1(10,10)); //20
alert(callSum2(10,10)); //20
在上面這個(gè)例子中,callSum1()在執(zhí)行sum()函數(shù)時(shí)傳入了this 作為this 值(因?yàn)槭窃谌肿饔糜蛑姓{(diào)用的,所以傳入的就是window 對(duì)象)和arguments 對(duì)象。而callSum2 同樣也調(diào)用了sum()函數(shù),但它傳入的則是this 和一個(gè)參數(shù)數(shù)組。這兩個(gè)函數(shù)都會(huì)正常執(zhí)行并返回正確的結(jié)果。
在嚴(yán)格模式下,未指定環(huán)境對(duì)象而調(diào)用函數(shù),則this 值不會(huì)轉(zhuǎn)型為window。除非明確把函數(shù)添加到某個(gè)對(duì)象或者調(diào)用apply()或call(),否則this 值將是undefined
3、不同點(diǎn)
call()方法與apply()方法的作用相同,它們的區(qū)別僅在于接收參數(shù)的方式不同。對(duì)于call()方法而言,第一個(gè)參數(shù)是this 值沒(méi)有變化,變化的是其余參數(shù)都直接傳遞給函數(shù)。換句話說(shuō),在使用call()方法時(shí),傳遞給函數(shù)的參數(shù)必須逐個(gè)列舉出來(lái),如下面的例子所示。
function sum(num1, num2){
return num1 + num2;
}
function callSum(num1, num2){
return sum.call(this, num1, num2);
}
alert(callSum(10,10)); //20
在使用call()方法的情況下,callSum()必須明確地傳入每一個(gè)參數(shù)。結(jié)果與使用apply()沒(méi)有什么不同。至于是使用apply()還是call(),完全取決于你采取哪種給函數(shù)傳遞參數(shù)的方式最方便。如果你打算直接傳入arguments 對(duì)象,或者包含函數(shù)中先接收到的也是一個(gè)數(shù)組,那么使用apply()肯定更方便;否則,選擇call()可能更合適。(在不給函數(shù)傳遞參數(shù)的情況下,使用哪個(gè)方法都無(wú)所謂) 。
4、擴(kuò)充函數(shù)運(yùn)行的作用域
事實(shí)上,傳遞參數(shù)并非apply()和call()真正的用武之地;它們真正強(qiáng)大的地方是能夠擴(kuò)充函數(shù)
賴以運(yùn)行的作用域。下面來(lái)看一個(gè)例子。
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
這個(gè)例子是在前面說(shuō)明this 對(duì)象的示例基礎(chǔ)上修改而成的。這一次,sayColor()也是作為全局函數(shù)定義的,而且當(dāng)在全局作用域中調(diào)用它時(shí),它確實(shí)會(huì)顯示”red”——因?yàn)閷?duì)this.color 的求值會(huì)轉(zhuǎn)換成window.color 的求值。而sayColor.call(this)和sayColor.call(window),則是兩種顯式地在全局作用域中調(diào)用函數(shù)的方式,結(jié)果當(dāng)然都會(huì)顯示”red”。但是,當(dāng)運(yùn)行sayColor.call(o)時(shí),函數(shù)的執(zhí)行環(huán)境就不一樣了,因?yàn)榇藭r(shí)函數(shù)體內(nèi)的this 對(duì)象指向了o,于是結(jié)果顯示的是”blue”。使用call()(或apply())來(lái)擴(kuò)充作用域的最大好處,就是對(duì)象不需要與方法有任何耦合關(guān)系。
在前面例子的第一個(gè)版本中,我們是先將sayColor()函數(shù)放到了對(duì)象o 中,然后再通過(guò)o 來(lái)調(diào)用它的;而在這里重寫(xiě)的例子中,就不需要先前那個(gè)多余的步驟了。
5、bind()方法
最后再來(lái)說(shuō) bind() 函數(shù),上面講的無(wú)論是 call() 也好, apply() 也好,都是立馬就調(diào)用了對(duì)應(yīng)的函數(shù),而 bind() 不會(huì), bind() 會(huì)生成一個(gè)新的函數(shù),bind() 函數(shù)的參數(shù)跟 call() 一致,第一個(gè)參數(shù)也是綁定 this 的值,后面接受傳遞給函數(shù)的不定參數(shù)。 bind() 生成的新函數(shù)返回后,你想什么時(shí)候調(diào)就什么時(shí)候調(diào),
window.color = "red";
var o = { color: "blue" };
function sayColor(){
alert(this.color);
}
var objectSayColor = sayColor.bind(o);
objectSayColor(); //blue
在這里,sayColor()調(diào)用bind()并傳入對(duì)象o,創(chuàng)建了objectSayColor()函數(shù)。object-SayColor()函數(shù)的this 值等于o,因此即使是在全局作用域中調(diào)用這個(gè)函數(shù),也會(huì)看到”blue”。
支持bind()方法的瀏覽器有IE9+、Firefox 4+、Safari 5.1+、Opera 12+和Chrome。
二、call(),apply()的繼承和回調(diào)
類的繼承
先來(lái)看這個(gè)例子:
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構(gòu)造函數(shù)(也是函數(shù))在this對(duì)象下執(zhí)行,那么 webDever就有了Person的所有屬性和方法,test對(duì)象就能夠直接調(diào)用Person的方法以及屬性了
用于回調(diào)
call 和 apply在回調(diào)行數(shù)中也非常有用,很多時(shí)候我們?cè)陂_(kāi)發(fā)過(guò)程中需要對(duì)改變回調(diào)函數(shù)的執(zhí)行上下文,最常用的比如ajax或者定時(shí)什么的,一般情況下,Ajax都是全局的,也就是window對(duì)象下的,來(lái)看這個(gè)例子:
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對(duì)象中的name屬性了。
三 、回調(diào)函數(shù)
說(shuō)起回調(diào)函數(shù),好多人雖然知道意思,但是還是一知半解。至于怎么用,還是有點(diǎn)糊涂。網(wǎng)上的一些相關(guān)的也沒(méi)有詳細(xì)的說(shuō)一下是怎么回事,說(shuō)的比較片面。下面我只是說(shuō)說(shuō)個(gè)人的一點(diǎn)理解,大牛勿噴。
定義
回調(diào)是什么?
看維基的 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中,回調(diào)函數(shù)具體的定義為:函數(shù)A作為參數(shù)(函數(shù)引用)傳遞到另一個(gè)函數(shù)B中,并且這個(gè)函數(shù)B執(zhí)行函數(shù)A。我們就說(shuō)函數(shù)A叫做回調(diào)函數(shù)。如果沒(méi)有名稱(函數(shù)表達(dá)式),就叫做匿名回調(diào)函數(shù)。
舉個(gè)例子:
你有事去隔壁寢室找同學(xué),發(fā)現(xiàn)人不在,你怎么辦呢?
方法1,每隔幾分鐘再去趟隔壁寢室,看人在不
方法2,拜托與他同寢室的人,看到他回來(lái)時(shí)叫一下你
前者是輪詢,后者是回調(diào)。
那你說(shuō),我直接在隔壁寢室等到同學(xué)回來(lái)可以嗎?
可以啊,只不過(guò)這樣原本你可以省下時(shí)間做其他事,現(xiàn)在必須浪費(fèi)在等待上了。
把原來(lái)的非阻塞的異步調(diào)用變成了阻塞的同步調(diào)用。
JavaScript的回調(diào)是在異步調(diào)用場(chǎng)景下使用的,使用回調(diào)性能好于輪詢。
因此callback 不一定用于異步,一般同步(阻塞)的場(chǎng)景下也經(jīng)常用到回調(diào),比如要求執(zhí)行某些操作后執(zhí)行回調(diào)函數(shù)。
一個(gè)同步(阻塞)中使用回調(diào)的例子,目的是在func1代碼執(zhí)行完成后執(zhí)行func2。
var func1=function(callback){
//do something.
(callback && typeof(callback) === "function") && callback();
}
func1(func2);
var func2=function(){
}
異步回調(diào)的例子:
$(document).ready(callback);
$.ajax({
url: "test.html",
context: document.body
}).done(function() {
$(this).addClass("done");
}).fail(function() { alert("error");
}).always(function() { alert("complete");
});
回調(diào)什么時(shí)候執(zhí)行
回調(diào)函數(shù),一般在同步情境下是最后執(zhí)行的,而在異步情境下有可能不執(zhí)行,因?yàn)槭录](méi)有被觸發(fā)或者條件不滿足。另外,最好保證回調(diào)存在且必須是函數(shù)引用或者函數(shù)表達(dá)式:
(callback && typeof(callback) === "function") && callback();
我們來(lái)看一下一個(gè)粗略的一個(gè)定義“函數(shù)a有一個(gè)參數(shù),這個(gè)參數(shù)是個(gè)函數(shù)b,當(dāng)函數(shù)a執(zhí)行完以后執(zhí)行函數(shù)b。那么這個(gè)過(guò)程就叫回調(diào)。”,這句話的意思是函數(shù)b以一個(gè)參數(shù)的形式傳入函數(shù)a并執(zhí)行,順序是先執(zhí)行a ,然后執(zhí)行參數(shù)b,b就是所謂的回調(diào)函數(shù)。我們先來(lái)看下面的例子。
function a(callback){
alert('a');
callback.call(this);//或者是 callback(), callback.apply(this),看個(gè)人喜好
}
function b(){
alert('b');
}
//調(diào)用
a(b);
這樣的結(jié)果是先彈出 ‘a(chǎn)',再?gòu)棾觥産'。這樣估計(jì)會(huì)有人問(wèn)了“寫(xiě)這樣的代碼有什么意思呢?好像沒(méi)太大的作用呢!”
是的,其實(shí)我也覺(jué)得這樣寫(xiě)沒(méi)啥意思,“如果調(diào)用一個(gè)函數(shù)就直接在函數(shù)里面調(diào)用它不就行了”。我這只是給大家寫(xiě)個(gè)小例子,做初步的理解。真正寫(xiě)代碼的過(guò)程中很少用這樣無(wú)參數(shù)的,因?yàn)樵诖蟛糠謭?chǎng)景中,我們要傳遞參數(shù)。來(lái)個(gè)帶參數(shù)的:
function c(callback){
alert('c');
callback.call(this,'d');
}
//調(diào)用
c(function(e){
alert(e);
});
這個(gè)調(diào)用看起來(lái)是不是似曾相識(shí),這里e參數(shù)被賦值為'd',我們只是簡(jiǎn)單的賦值為字符竄,其實(shí)也可以賦值為對(duì)象。Jquery里面是不是也有個(gè)e參數(shù)?
回調(diào)函數(shù)的使用場(chǎng)合
- 資源加載:動(dòng)態(tài)加載js文件后執(zhí)行回調(diào),加載iframe后執(zhí)行回調(diào),ajax操作回調(diào),圖片加載完成執(zhí)行回調(diào),AJAX等等。
- DOM事件及Node.js事件基于回調(diào)機(jī)制(Node.js回調(diào)可能會(huì)出現(xiàn)多層回調(diào)嵌套的問(wèn)題)。
- setTimeout的延遲時(shí)間為0,這個(gè)hack經(jīng)常被用到,settimeout調(diào)用的函數(shù)其實(shí)就是一個(gè)callback的體現(xiàn)
- 鏈?zhǔn)秸{(diào)用:鏈?zhǔn)秸{(diào)用的時(shí)候,在賦值器(setter)方法中(或者本身沒(méi)有返回值的方法中)很容易實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用,而取值器(getter)相對(duì)來(lái)說(shuō)不好實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用,因?yàn)槟阈枰≈灯鞣祷啬阈枰臄?shù)據(jù)而不是this指針,如果要實(shí)現(xiàn)鏈?zhǔn)椒椒?,可以用回調(diào)函數(shù)來(lái)實(shí)現(xiàn)
- setTimeout、setInterval的函數(shù)調(diào)用得到其返回值。由于兩個(gè)函數(shù)都是異步的,即:他們的調(diào)用時(shí)序和程序的主流程是相對(duì)獨(dú)立的,所以沒(méi)有辦法在主體里面等待它們的返回值,它們被打開(kāi)的時(shí)候程序也不會(huì)停下來(lái)等待,否則也就失去了setTimeout及setInterval的意義了,所以用return已經(jīng)沒(méi)有意義,只能使用callback。callback的意義在于將timer執(zhí)行的結(jié)果通知給代理函數(shù)進(jìn)行及時(shí)處理。
當(dāng)函數(shù)的實(shí)現(xiàn)過(guò)程非常漫長(zhǎng),你是選擇等待函數(shù)完成處理,還是使用回調(diào)函數(shù)進(jìn)行異步處理呢?這種情況下,使用回調(diào)函數(shù)變得至關(guān)重要,例如:AJAX請(qǐng)求。若是使用回調(diào)函數(shù)進(jìn)行處理,代碼就可以繼續(xù)進(jìn)行其他任務(wù),而無(wú)需空等。實(shí)際開(kāi)發(fā)中,經(jīng)常在javascript中使用異步調(diào)用,甚至在這里強(qiáng)烈推薦使用!
下面有個(gè)更加全面的使用AJAX加載XML文件的示例,并且使用了call()函數(shù),在請(qǐng)求對(duì)象(requested object)上下文中調(diào)用回調(diào)函數(shù)。
function fn(url, callback){
var httpRequest; //創(chuàng)建XHR
httpRequest = window.XMLHttpRequest ? new XMLHttpRequest() :
window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : undefined;
//針對(duì)IE進(jìn)行功能性檢測(cè)
httpRequest.onreadystatechange = function(){
if(httpRequest.readystate === 4 && httpRequest.status === 200){ //狀態(tài)判斷
callback.call(httpRequest.responseXML);
}
};
httpRequest.open("GET", url);
httpRequest.send();
}
fn("text.xml", function(){ //調(diào)用函數(shù)
console.log(this); //此語(yǔ)句后輸出
});
console.log("this will run before the above callback."); //此語(yǔ)句先輸出
我們請(qǐng)求異步處理,意味著我們開(kāi)始請(qǐng)求時(shí),就告訴它們完成之時(shí)調(diào)用我們的函數(shù)。在實(shí)際情況中,onreadystatechange事件處理程序還得考慮請(qǐng)求失敗的情況,這里我們是假設(shè)xml文件存在并且能被瀏覽器成功加載。這個(gè)例子中,異步函數(shù)分配給了onreadystatechange事件,因此不會(huì)立刻執(zhí)行。
最終,第二個(gè)console.log語(yǔ)句先執(zhí)行,因?yàn)榛卣{(diào)函數(shù)直到請(qǐng)求完成才執(zhí)行。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助。
詳細(xì)介紹請(qǐng)查看: 《詳解JavaScript的回調(diào)函數(shù)》
- JavaScript中的apply()方法和call()方法使用介紹
- js中繼承的幾種用法總結(jié)(apply,call,prototype)
- javascript中apply和call方法的作用及區(qū)別說(shuō)明
- 詳解js中的apply與call的用法
- JS中改變this指向的方法(call和apply、bind)
- JavaScript中的apply和call函數(shù)詳解
- js中call與apply的用法小結(jié)
- js apply/call/caller/callee/bind使用方法與區(qū)別分析
- JS面向?qū)ο?、prototype、call()、apply()
- JavaScript學(xué)習(xí)點(diǎn)滴 call、apply的區(qū)別
- 詳解JS中的this、apply、call、bind(經(jīng)典面試題)
- 淺談javascript中call()、apply()、bind()的用法
- JavaScript中apply與call的用法意義及區(qū)別說(shuō)明
- js中apply()和call()的區(qū)別與用法實(shí)例分析
- JavaScript中的this,call,apply使用及區(qū)別詳解
- javascript call和apply方法
- 深入理解JavaScript中的call、apply、bind方法的區(qū)別
- 再談JavaScript中bind、call、apply三個(gè)方法的區(qū)別與使用方式
相關(guān)文章
JS彈出可拖拽可關(guān)閉的div層完整實(shí)例
這篇文章主要介紹了JS彈出可拖拽可關(guān)閉的div層完整實(shí)現(xiàn)方法,包括對(duì)div彈出層的樣式及功能的實(shí)現(xiàn)技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-02-02
前端uniapp封裝網(wǎng)絡(luò)請(qǐng)求以及實(shí)際應(yīng)用教程
這篇文章主要給大家介紹了關(guān)于前端uniapp封裝網(wǎng)絡(luò)請(qǐng)求以及實(shí)際應(yīng)用的相關(guān)資料,在uniapp中進(jìn)行網(wǎng)絡(luò)測(cè)試請(qǐng)求可以通過(guò)封裝網(wǎng)絡(luò)請(qǐng)求來(lái)實(shí)現(xiàn),文中給出了詳細(xì)的代碼實(shí)例,需要的朋友可以參考下2024-01-01
JavaScript對(duì)JSON數(shù)據(jù)進(jìn)行排序和搜索
今天教給大家如何使用數(shù)組的方法來(lái)實(shí)現(xiàn)JSON數(shù)據(jù)進(jìn)行排序和搜索功能,具體實(shí)例代碼大家參考下本文吧2017-07-07
Javascript基礎(chǔ)之?dāng)?shù)組的使用
這篇文章主要介紹了Javascript基礎(chǔ)之?dāng)?shù)組的使用的相關(guān)資料,介紹的非常詳解,具有參考借鑒價(jià)值,感興趣的朋友一起學(xué)習(xí)吧2016-05-05
js實(shí)現(xiàn)刪除li標(biāo)簽一行內(nèi)容
這篇文章主要為大家詳細(xì)介紹了js實(shí)現(xiàn)刪除li標(biāo)簽一行內(nèi)容,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-04-04
JS實(shí)現(xiàn)簡(jiǎn)單易用的手機(jī)端浮動(dòng)窗口顯示效果
這篇文章主要介紹了JS實(shí)現(xiàn)簡(jiǎn)單易用的手機(jī)端浮動(dòng)窗口顯示效果,涉及javascript針對(duì)頁(yè)面元素的動(dòng)態(tài)操作相關(guān)技巧,適用于做廣告展示,需要的朋友可以參考下2016-09-09

