JavaScript基礎(chǔ)篇(6)之函數(shù)表達式閉包
其實js支持函數(shù)閉包的主要原因是因為js需要函數(shù)能夠保存數(shù)據(jù)。這里的保存數(shù)據(jù)是只函數(shù)在運行結(jié)束以后函數(shù)內(nèi)變量的值也會進行保存。至于為什么js需要在函數(shù)內(nèi)可以保存數(shù)據(jù),那就是js是一種函數(shù)式語言。在函數(shù)內(nèi)保存數(shù)據(jù)是函數(shù)式語言的一大特征。
回顧前面介紹過的三種定義函數(shù)方式
functiosu(numnumreturnunum//函數(shù)聲明語法定義
vasufunction(numnum)returnunum}//函數(shù)表達式定義
vasuneFunction("num""num""returnunum")//Functio構(gòu)造函數(shù)
在分析閉包之前我們先來看看,定義和調(diào)用函數(shù)容易犯的錯誤。
例1:
sayHi(); //錯誤:函數(shù)還不存在
var sayHi = function () {
alert("test");
};
例2:
if (true) {
function sayHi() {
alert("1");
}
} else {
function sayHi() {
alert("2");
}
}
sayHi();//打印結(jié)果并不是我們想要的
例3:
var fun1 = function fun2() {
alert("test");
}
fun2();//錯誤:函數(shù)還不存在
在例1中,我們不能在使用函數(shù)聲明式語法定義之前調(diào)用函數(shù)。解決方案:
1.如果使用函數(shù)表達式定義函數(shù)的話,需要在表達式定義后調(diào)用。
var sayHi = function () {
alert("test");
};
sayHi()
2.使用函數(shù)聲明式。(這里瀏覽器引擎會 函數(shù)聲明提升, 在所有代碼執(zhí)行之前先讀取函數(shù)聲明)
sayHi();
function sayHi () {
alert("test");
};
在例2中,我們預(yù)期的結(jié)果應(yīng)該是打印1,實際結(jié)果是打印2。
if (true) {
function sayHi() {
alert("1");
}
} else {
function sayHi() {
alert("2");
}
}
sayHi();//打印結(jié)果并不是我們想要的
為什么會這樣?正因為 函數(shù)聲明提升 ,所以瀏覽器在預(yù)解析的時候不會判斷if條件,直接解析第二個函數(shù)定義的時候覆蓋了第一個。
解決方案:
var sayHi;
if (true) {
sayHi = function () {
alert("1");
}
} else {
sayHi = function () {
alert("2");
}
}
sayHi();
在例3中,發(fā)現(xiàn)只能只用fun1()調(diào)用,而不能使用fun2()調(diào)用。
我自己的理解,真正原因不知道。沒找到資料。
因為1: function fun3() { }; 等效與 var fun3 = function fun3() { }; 如圖:
所以只能只用fun1()調(diào)用,而不能使用fun2()調(diào)用。
其實這里我還是有疑問的?哪位大神知道,望告知。
既然,fun2在外面不能調(diào)用為什么在函數(shù)內(nèi)部能調(diào)用?雖然在debugger還是得不到fun1。
好了,通過上面的三道題目熱身。我們繼續(xù)今天的主題“閉包”。
1.什么是閉包?
定義:就是有權(quán)訪問另一個函數(shù)作用域的變量的函數(shù)
我們先從一個示例函數(shù)開始:
例1:
function fun() {
var a = "張三";
}
fun();//在我們執(zhí)行完后,變量a就被標(biāo)記為銷毀了
例2:
function fun() {
var a = "張三";
return function () {
alert("test");
}
}
var f = fun();//同樣,在我們執(zhí)行完后,變量a就被標(biāo)記為銷毀了
例3:
function fun() {
var a = "張三";
return function () {
alert(a);
}
}
var f = fun();//【現(xiàn)在情況發(fā)生變化了,如果a被銷毀,顯然f被調(diào)用的話就不能訪問到變量a的值了】
f();//【然后變量a的值正常的被訪問到了】
//這就是閉包,當(dāng)函數(shù)A 返回的函數(shù)B 里面使用到了函數(shù)A的變量,那么函數(shù)B就使用了閉包。
示例:
function fun() {
var a = "張三";
return function () {
alert(a);
}
}
var f = fun();//【現(xiàn)在情況發(fā)生變化了,如果a被銷毀,顯然f被調(diào)用的話就不能訪問到變量a的值了】
f();//【然后變量a的值正常的被訪問到了】
顯然,濫用閉包會增大內(nèi)存的使用。所以非特殊情況盡量不要使用閉包。如果用到了,記得手動設(shè)置空引用,內(nèi)存才能被回收 f = null ;
圖解:(不了解作用域鏈的同學(xué)請先看前面的文章 作用域和作用域鏈 )
2.什么是匿名函數(shù)? (僅僅只是解釋這個概念)
如:(即,沒有名字的函數(shù))
關(guān)于對象中函數(shù)的返回值是匿名函數(shù)時,this的怪異現(xiàn)象
講解之前,先清醒下頭腦,不要越看越迷糊了。如果迷糊了,那就直接忽略下面的。
var name1 = "張三";
var obj = {
name1: "李四",
fun2: function () {
alert(this.name1);
},
fun3: function () {
return function () {
alert(this.name1);
}
}
}
obj.fun2();//打印結(jié)果"李四"意料之中的。
obj.fun3()();//因為這里返回的是一個函數(shù),所以要再加一對()來調(diào)用。打印結(jié)果是"張三",意料之外。
//真是百事不得其解啊,什么this指向了全局?
我們前面講過“ 哪個對象點出來的方法,this就是哪個對象 ”,那我們的 obj.fun3()() 打印的是“張三”也就是說this執(zhí)行了全局作用域。
我們看看下面的示例也許就知道為什么了。
var name1 = "張三";
var obj = {
name1: "李四",
fun2: function () {
alert(this.name1);
},
fun3: function () {
return function () {
alert(this.name1);
}
}
}
//obj.fun3()();
var obj2 = {};
obj2.name1 = "test";
obj2.fun = obj.fun3();
obj2.fun();//打印結(jié)果"test",再次證明了“哪個對象點出來的方法,this就是哪個對象”.
var name1 = "張三";
var obj = {
name1: "李四",
fun2: function () {
alert(this.name1);
},
fun3: function () {
return function () {
alert(this.name1);
}
}
}
//obj.fun3()();
var obj2 = {};
obj2.name1 = "test";
obj2.fun = obj.fun3();
obj2.fun();//打印結(jié)果"test",再次證明了“哪個對象點出來的方法,this就是哪個對象”.
我們來分解下 obj.fun3()() 先是 obj.fun3() 返回一個匿名函數(shù)到了window作用域,然后接著調(diào)用this就指向了window了。( 感覺解釋有點勉強,也不知道對不,暫時自己先是這么理解的 )
閉包形成的原因:內(nèi)存釋放問題
一般,當(dāng)函數(shù)執(zhí)行完畢后,局部活動對象會被銷毀,內(nèi)存中僅保存全局作用域,但閉包的情況是不一樣的。
閉包的活動對象依然會保存在內(nèi)存中,于是像上例中,函數(shù)調(diào)用返回后,變量i是屬于活動對象里面的,就是說其棧區(qū)還沒有釋放,但你調(diào)用c()的時候i變量保存的作用域鏈從b()->a()->全局去尋找作用域var i聲明所在,然后找到了var i=1;然后在閉包內(nèi)++i;結(jié)果,最后輸出的值就是2了;
以上所述是小編給大家分享的JavaScript基礎(chǔ)篇(6)之函數(shù)表達式閉包,希望大家喜歡。
相關(guān)文章
不使用XMLHttpRequest實現(xiàn)異步加載 Iframe和script
運用Iframe和script可以實現(xiàn)簡單的異步加載,沒有使用XMLHttpRequest,需要的朋友可以參考下2012-10-10
javascript 動態(tài)創(chuàng)建表格的2種方法總結(jié)
本文給大家總結(jié)了2種使用javascript動態(tài)創(chuàng)建表格的方法,非常簡單實用,小伙伴們自己對比下看看哪種更適合你呢,一般本人是使用第一種的。2015-03-03
bootstrap datepicker 與bootstrapValidator同時使用時選擇日期后無法正常觸發(fā)校驗的解
這篇文章主要介紹了bootstrap datepicker 與bootstrapValidator同時使用時選擇日期后無法正常觸發(fā)校驗的解決思路的相關(guān)資料,需要的朋友可以參考下2016-09-09
JavaScript中的await函數(shù)使用小結(jié)
async 函數(shù)是 AsyncFunction 構(gòu)造函數(shù)的實例,并且其中允許使用 await 關(guān)鍵字,async 和 await 關(guān)鍵字讓我們可以用一種更簡潔的方式寫出基于 Promise 的異步行為,而無需刻意地鏈?zhǔn)秸{(diào)用 promise,這篇文章主要介紹了JavaScript中的await,需要的朋友可以參考下2024-01-01

