久久婷婷香蕉热狠狠综合,精品无码国产自产拍在线观看蜜,寡妇房东在做爰3,中文字幕日本人妻久久久免费,国产成人精品三上悠亚久久

當前位置:首頁 > 嵌入式培訓 > 嵌入式學習 > 講師博文 > javascript閉包(bao)闡述

javascript閉包闡述 時間:2018-09-28      來源:未知

前言:

眾所(suo)周知,javascript中(zhong)的閉包很(hen)重要,但真正理解其語法含義,還存在很(hen)多不足,所(suo)以在此從各個(ge)方面講解Javascript中(zhong)閉包涉及的一些含義:主要包括作用域機制、閉包等待

作用域(yu)在(zai)JavaScript程序員(yuan)日常使用中有不同的含義,如下所(suo)示:

• this綁定的值;

• this綁(bang)定(ding)的值定(ding)義的執行上下文;

• 一個變量的“生(sheng)命周(zhou)期”;

• 變(bian)量(liang)的值解析方案,或詞法綁定。

下面(mian)將講訴JavaScript作(zuo)用域概念(nian),由此(ci)引出變(bian)量值解析(xi)方(fang)案的一般想法,后(hou)再(zai)探(tan)討JavaScript里閉包這一重(zhong)要知識點。

1.全局(ju)作用(yong)域

所有瀏(liu)覽器都支持(chi) window 對(dui)(dui)象(xiang)(xiang),它表示瀏(liu)覽器窗口,JavaScript 全(quan)局(ju)(ju)對(dui)(dui)象(xiang)(xiang)、函數以(yi)及變量均自動成(cheng)為 window 對(dui)(dui)象(xiang)(xiang)的(de)成(cheng)員。所以(yi),全(quan)局(ju)(ju)變量是 window 對(dui)(dui)象(xiang)(xiang)的(de)屬性,全(quan)局(ju)(ju)函數是 window 對(dui)(dui)象(xiang)(xiang)的(de)方(fang)法,甚至 HTML DOM 的(de) document 也是 window 對(dui)(dui)象(xiang)(xiang)的(de)屬性之一。

全(quan)局變(bian)量(liang)是JavaScript里生命(ming)周(zhou)期(一個(ge)變(bian)量(liang)多(duo)長(chang)(chang)時間內保持一定(ding)的值)長(chang)(chang)的變(bian)量(liang),其將跨越整個(ge)程序,可以被程序中的任何函數方法訪問。

在全局下聲明的變量都(dou)會(hui)在window對象下,都(dou)在全局作用域(yu)中,我(wo)們可以通過window對象訪(fang)問,也(ye)可以直接訪(fang)問。

1 var name = "jeri";

2 console.log(window.name); // 輸出:jeri

3 console.log(name); // 輸出:jeri

在JS中任何位置,沒有使用var關鍵字聲(sheng)明(ming)的變(bian)量也都是全局(ju)變(bian)量。

1 function fun() {

2 name = "jeri";

3 alert(name);

4 }

5

6 console.log(name); // 輸出:jeri

全(quan)局(ju)變(bian)(bian)量(liang)(liang)(liang)存在(zai)(zai)于整個函(han)(han)數(shu)的生(sheng)命(ming)周期中(zhong),然而其在(zai)(zai)全(quan)局(ju)范(fan)圍(wei)內很容(rong)易被篡改,我們在(zai)(zai)使(shi)(shi)用全(quan)局(ju)變(bian)(bian)量(liang)(liang)(liang)時一定要(yao)小心,盡(jin)量(liang)(liang)(liang)不要(yao)使(shi)(shi)用全(quan)局(ju)變(bian)(bian)量(liang)(liang)(liang)。在(zai)(zai)函(han)(han)數(shu)內部聲明(ming)變(bian)(bian)量(liang)(liang)(liang)沒有使(shi)(shi)用var也會產生(sheng)全(quan)局(ju)變(bian)(bian)量(liang)(liang)(liang),會為我們造(zao)成(cheng)一些混亂,比如變(bian)(bian)量(liang)(liang)(liang)覆蓋等。所以,我們在(zai)(zai)聲明(ming)變(bian)(bian)量(liang)(liang)(liang)的任何時候好都要(yao)帶上var。

全局變量(liang)存在于程(cheng)序的整(zheng)個生命周期,但并不是通過其引用我們(men)一定可以訪問到全局變量(liang)。

2.詞法作用域(yu)

詞(ci)法(fa)作(zuo)(zuo)(zuo)(zuo)用(yong)域(yu)(yu):函數在定義(yi)它們的(de)(de)作(zuo)(zuo)(zuo)(zuo)用(yong)域(yu)(yu)里(li)運行,而不(bu)是在執行它們的(de)(de)作(zuo)(zuo)(zuo)(zuo)用(yong)域(yu)(yu)里(li)運行。也就是說詞(ci)法(fa)作(zuo)(zuo)(zuo)(zuo)用(yong)域(yu)(yu)取決于源碼,通過(guo)靜態(tai)分析(xi)就能確定,因此詞(ci)法(fa)作(zuo)(zuo)(zuo)(zuo)用(yong)域(yu)(yu)也叫做靜態(tai)作(zuo)(zuo)(zuo)(zuo)用(yong)域(yu)(yu)。with和eval除外,所以(yi)只能說JS的(de)(de)作(zuo)(zuo)(zuo)(zuo)用(yong)域(yu)(yu)機制非常接近詞(ci)法(fa)作(zuo)(zuo)(zuo)(zuo)用(yong)域(yu)(yu)(Lexical scope)。詞(ci)法(fa)作(zuo)(zuo)(zuo)(zuo)用(yong)域(yu)(yu)也可以(yi)理解為一個(ge)變量的(de)(de)可見性,及其文(wen)本(ben)表述的(de)(de)模擬(ni)值。

1 var name = "global";

2

3 function fun() {

4 var name = "jeri";

5 return name;

6 }

7

8 console.log(fun()); // 輸出:jeri

9 console.log(name); // 輸出(chu):global

在通(tong)常(chang)情況下,變量的(de)(de)查(cha)(cha)詢從近接的(de)(de)綁定上下文(wen)開始,向外部逐漸擴展,直到查(cha)(cha)詢到第(di)一個(ge)綁定,一旦完成查(cha)(cha)找(zhao)(zhao)就(jiu)結束(shu)搜索(suo)。就(jiu)像上例,先查(cha)(cha)找(zhao)(zhao)離(li)它近的(de)(de)name="jeri",查(cha)(cha)詢完成后就(jiu)結束(shu)了,將第(di)一個(ge)獲取的(de)(de)值(zhi)作為變量的(de)(de)值(zhi)。

3.動態作用域(yu)

在編(bian)程實踐(jian)中(zhong),容易低估(gu)和過度濫(lan)用的概念就是動(dong)態(tai)作用域,因為很少有語言支持(chi)這種方式為綁(bang)定解析方案(an)。

動(dong)態作用域(yu)(yu)(yu)與詞(ci)(ci)法作用域(yu)(yu)(yu)相對而言的(de),不同于詞(ci)(ci)法作用域(yu)(yu)(yu)在定(ding)(ding)義時確(que)定(ding)(ding),動(dong)態作用域(yu)(yu)(yu)在執行(xing)時確(que)定(ding)(ding),其生存周期到代碼片段執行(xing)為(wei)止。動(dong)態變量存在于動(dong)態作用域(yu)(yu)(yu)中(zhong),任何給定(ding)(ding)的(de)綁定(ding)(ding)的(de)值,在確(que)定(ding)(ding)調用其函數之前(qian),都(dou)是不可知(zhi)的(de)。

在代碼執(zhi)行時,對(dui)應的(de)(de)作用域(yu)(yu)鏈(lian)常常是(shi)保持靜態(tai)的(de)(de)。然(ran)而當遇(yu)到(dao)(dao)with語(yu)句(ju)、call方法(fa)、apply方法(fa)和try-catch中的(de)(de)catch時,會(hui)改變作用域(yu)(yu)鏈(lian)的(de)(de)。以with為例,在遇(yu)到(dao)(dao)with語(yu)句(ju)時,會(hui)將(jiang)傳入的(de)(de)對(dui)象屬性作為局(ju)部變量來(lai)顯(xian)示(shi),使其便于訪問,也(ye)就(jiu)是(shi)說把一個新(xin)的(de)(de)對(dui)象添(tian)加到(dao)(dao)了作用域(yu)(yu)鏈(lian)的(de)(de)頂端(duan),這樣必然(ran)影響對(dui)局(ju)部標志符的(de)(de)解析(xi)。當with語(yu)句(ju)執(zhi)行完畢后,會(hui)把作用域(yu)(yu)鏈(lian)恢復到(dao)(dao)原始狀態(tai)。實例如下:

1 var name = "global";

2

3 // 使用(yong)with之(zhi)前

4 console.log(name); // 輸出:global

5

6 with({name:"jeri"}){

7 console.log(name); // 輸出(chu):jeri

8 }

9

10 // 使(shi)用(yong)with之后,作(zuo)用(yong)域鏈恢復

11 console.log(name); // 輸出:global

在作(zuo)用(yong)域鏈(lian)中有動態作(zuo)用(yong)域時(shi),this引(yin)用(yong)也會(hui)變得更加復雜,不再指(zhi)向第一(yi)次創建時(shi)的(de)上下文,而是由調用(yong)者確定。比(bi)如(ru)在使用(yong)apply或call方法時(shi),傳入它們的(de)第一(yi)個參(can)數就是被引(yin)用(yong)的(de)對象。實(shi)例如(ru)下:

1 function globalThis() {

2 console.log(this);

3 }

4

5 globalThis(); // 輸出:Window {document: document,external: Object…}

 6 globalThis.call({name:"jeri"}); // 輸出:Object {name: "jeri"}

7 globalThis.apply({name:"jeri"},[]); // 輸出:Object {name: "jeri"}

因為this引用(yong)(yong)是(shi)動態(tai)作用(yong)(yong)域,所以在(zai)編程過程中(zhong)一定(ding)要注意this引用(yong)(yong)的(de)(de)變化,及時跟蹤this的(de)(de)變動。

4.函數作(zuo)用域

函(han)(han)數(shu)(shu)作用(yong)(yong)域(yu),顧名思義就是在定(ding)義函(han)(han)數(shu)(shu)時候(hou)產生的(de)作用(yong)(yong)域(yu),這個作用(yong)(yong)域(yu)也可以稱為局(ju)部作用(yong)(yong)域(yu)。和全(quan)局(ju)作用(yong)(yong)域(yu)相反,函(han)(han)數(shu)(shu)作用(yong)(yong)域(yu)一般只在函(han)(han)數(shu)(shu)的(de)代碼片段內(nei)可訪(fang)問到,外部不能進(jin)行變(bian)量訪(fang)問。在函(han)(han)數(shu)(shu)內(nei)部定(ding)義的(de)變(bian)量存在于(yu)函(han)(han)數(shu)(shu)作用(yong)(yong)域(yu)中(zhong),其生命周期隨著函(han)(han)數(shu)(shu)的(de)執行結(jie)束而結(jie)束。實(shi)例如下:

1 var name = "global";

2

3 function fun() {

4 var name = "jeri";

5 console.log(name); // 輸出:jeri

6

7 with ({name:"with"}) {

8 console.log(name); // 輸出(chu):with

9 }

10 console.log(name); // 輸出:jeri

11 }

12

13 fun();

14

15 // 不(bu)能訪問函數作用(yong)域

16 console.log(name); // 輸出(chu):global

5.沒有塊級作用域

不(bu)同(tong)于其他編程語言,在(zai)(zai)JavaScript里并沒有塊級作用(yong)域(yu),也就(jiu)是說在(zai)(zai)for、if、while等(deng)語句內部(bu)的(de)聲(sheng)明(ming)的(de)變量(liang)與(yu)在(zai)(zai)外部(bu)聲(sheng)明(ming)是一(yi)樣的(de),在(zai)(zai)這(zhe)些語句外部(bu)也可以訪問和修(xiu)改這(zhe)些變量(liang)的(de)值。實例如下:

1 function fun() {

2

3 if(0 < 2) {

4 var name = "jeri";

5 }

6 console.log(name); // 輸出(chu):jeri

7 name = "change";

8 console.log(name); // 輸出:change

9 }

10

11 fun();

6.作用域鏈

JavaScript里一切(qie)皆為(wei)對(dui)(dui)象(xiang),包(bao)括(kuo)函(han)數(shu)(shu)。函(han)數(shu)(shu)對(dui)(dui)象(xiang)和其(qi)它對(dui)(dui)象(xiang)一樣,擁(yong)有可以通過(guo)代碼訪問(wen)(wen)的(de)(de)(de)屬性和一系列僅供JavaScript引擎訪問(wen)(wen)的(de)(de)(de)內部屬性。其(qi)中一個內部屬性是作用(yong)域(yu)(yu),包(bao)含了函(han)數(shu)(shu)被創(chuang)建的(de)(de)(de)作用(yong)域(yu)(yu)中對(dui)(dui)象(xiang)的(de)(de)(de)集合,稱為(wei)函(han)數(shu)(shu)的(de)(de)(de)作用(yong)域(yu)(yu)鏈,它用(yong)來保證對(dui)(dui)執行環境有權訪問(wen)(wen)的(de)(de)(de)變(bian)量和函(han)數(shu)(shu)的(de)(de)(de)有序(xu)訪問(wen)(wen)。

當(dang)一(yi)個函數(shu)(shu)(shu)(shu)(shu)創(chuang)建(jian)后,它的(de)(de)(de)(de)作(zuo)用(yong)(yong)域(yu)(yu)鏈(lian)會(hui)(hui)被創(chuang)建(jian)此函數(shu)(shu)(shu)(shu)(shu)的(de)(de)(de)(de)作(zuo)用(yong)(yong)域(yu)(yu)中可訪問的(de)(de)(de)(de)數(shu)(shu)(shu)(shu)(shu)據(ju)對(dui)象(xiang)填(tian)充。在全局作(zuo)用(yong)(yong)域(yu)(yu)中創(chuang)建(jian)的(de)(de)(de)(de)函數(shu)(shu)(shu)(shu)(shu),其作(zuo)用(yong)(yong)域(yu)(yu)鏈(lian)會(hui)(hui)自(zi)動(dong)成為全局作(zuo)用(yong)(yong)域(yu)(yu)中的(de)(de)(de)(de)一(yi)員。而當(dang)函數(shu)(shu)(shu)(shu)(shu)執行(xing)(xing)時(shi),其活(huo)動(dong)對(dui)象(xiang)就會(hui)(hui)成為作(zuo)用(yong)(yong)域(yu)(yu)鏈(lian)中的(de)(de)(de)(de)第(di)一(yi)個對(dui)象(xiang)(活(huo)動(dong)對(dui)象(xiang):對(dui)象(xiang)包(bao)含了(le)函數(shu)(shu)(shu)(shu)(shu)的(de)(de)(de)(de)所有局部變量、命名(ming)參數(shu)(shu)(shu)(shu)(shu)、參數(shu)(shu)(shu)(shu)(shu)集(ji)合以(yi)及this)。在程序執行(xing)(xing)時(shi),Javascript引擎會(hui)(hui)通過搜索上下文的(de)(de)(de)(de)作(zuo)用(yong)(yong)域(yu)(yu)鏈(lian)來解析諸(zhu)如變量和函數(shu)(shu)(shu)(shu)(shu)名(ming)這樣的(de)(de)(de)(de)標識符。其會(hui)(hui)從作(zuo)用(yong)(yong)域(yu)(yu)鏈(lian)的(de)(de)(de)(de)里(li)面開始(shi)檢(jian)索,按照由內到(dao)(dao)外(wai)的(de)(de)(de)(de)順序,直到(dao)(dao)完(wan)成查找(zhao),一(yi)旦完(wan)成查找(zhao)就結束搜索。如果沒有查詢到(dao)(dao)標識符聲明,則報(bao)錯。當(dang)函數(shu)(shu)(shu)(shu)(shu)執行(xing)(xing)結束,運行(xing)(xing)期上下文被銷毀(hui),活(huo)動(dong)對(dui)象(xiang)也隨之銷毀(hui)。實例如下:

1 var name = 'global';

2

3 function fun() {

4 console.log(name); // output:global

5 name = "change";

6 // 函(han)數內部可以修(xiu)改全局(ju)變量

7 console.log(name); // output:change

8 // 先(xian)查詢活動對象

9 var age = "18";

10 console.log(age); // output:18

11 }

12

13 fun();

14

15 // 函數(shu)執(zhi)行完畢,執(zhi)行環境銷毀

16 console.log(age); // output:Uncaught ReferenceError: age is not defined

7.閉包

閉包是JavaScript的(de)一大謎團(tuan),關于(yu)這(zhe)個(ge)問題(ti)有很多文章進行講(jiang)述,然而依然有相當數量(liang)的(de)程序員(yuan)對這(zhe)個(ge)概念(nian)理解不透徹。閉包的(de)官方定義為(wei):一個(ge)擁有許多變(bian)量(liang)和綁定了這(zhe)些(xie)變(bian)量(liang)的(de)環(huan)境的(de)表達式(shi)(shi)(通(tong)常(chang)是一個(ge)函(han)數),因而這(zhe)些(xie)變(bian)量(liang)也是該表達式(shi)(shi)的(de)一部分。

一(yi)句(ju)話概括就(jiu)是:閉包就(jiu)是一(yi)個函數,捕獲作用(yong)域內的(de)外(wai)部綁(bang)(bang)定(ding)。這(zhe)些綁(bang)(bang)定(ding)是為之后使用(yong)而被(bei)綁(bang)(bang)定(ding),即(ji)使作用(yong)域已經銷毀(hui)。

自由變量

自(zi)由變量(liang)(liang)與閉(bi)包(bao)的(de)關系(xi)是(shi)(shi),自(zi)由變量(liang)(liang)閉(bi)合(he)于(yu)閉(bi)包(bao)的(de)創建。閉(bi)包(bao)背后(hou)的(de)邏輯是(shi)(shi),如(ru)果(guo)一個函(han)(han)(han)數(shu)內(nei)(nei)(nei)部(bu)有其(qi)他函(han)(han)(han)數(shu),那么這(zhe)些(xie)內(nei)(nei)(nei)部(bu)函(han)(han)(han)數(shu)可(ke)以(yi)(yi)訪問在(zai)這(zhe)個外部(bu)函(han)(han)(han)數(shu)中聲(sheng)明(ming)的(de)變量(liang)(liang)(這(zhe)些(xie)變量(liang)(liang)就(jiu)稱之(zhi)為自(zi)由變量(liang)(liang))。然而,這(zhe)些(xie)變量(liang)(liang)可(ke)以(yi)(yi)被(bei)內(nei)(nei)(nei)部(bu)函(han)(han)(han)數(shu)捕獲,從高(gao)階函(han)(han)(han)數(shu)(返回另一個函(han)(han)(han)數(shu)的(de)函(han)(han)(han)數(shu)稱為高(gao)階函(han)(han)(han)數(shu))中return語句實現(xian)“越獄”,以(yi)(yi)供以(yi)(yi)后(hou)使(shi)用。內(nei)(nei)(nei)部(bu)函(han)(han)(han)數(shu)在(zai)沒有任何局部(bu)聲(sheng)明(ming)之(zhi)前(qian)(既不是(shi)(shi)被(bei)傳(chuan)入,也不是(shi)(shi)局部(bu)聲(sheng)明(ming))使(shi)用的(de)變量(liang)(liang)就(jiu)是(shi)(shi)被(bei)捕獲的(de)變量(liang)(liang)。實例如(ru)下:

1 function makeAdder(captured) {

2 return function(free) {

3 var ret = free + captured;

4 console.log(ret);

5 }

6 }

7

8 var add10 = makeAdder(10);

9

10 add10(2); // 輸出:12

從上例可知,外(wai)部函(han)數中的(de)變量captured被執行加法的(de)返回函(han)數捕獲,內部函(han)數從未(wei)聲明(ming)過captured變量,卻(que)可以引(yin)用它。

如(ru)果我(wo)們(men)再(zai)創建一個加法器(qi)將(jiang)捕獲到(dao)同(tong)名變量captured,但有(you)不(bu)同(tong)的值,因為這個加法器(qi)是在調用makeAdder之后被(bei)創建:

1 var add16 = makeAdder(16);

2

3 add16(18); // 輸(shu)出:24

4

5 add10(10); // 輸出:20

如(ru)上述(shu)代碼(ma)所(suo)示,每一(yi)個(ge)新的加法器函數都(dou)保留了自(zi)己創建時捕獲(huo)的captured實例。

變量遮蔽

在JavaScript中,當變量在一定作(zuo)用(yong)域(yu)內聲(sheng)明,然后(hou)在另一個同名變量在一個較低的作(zuo)用(yong)域(yu)聲(sheng)明,會(hui)發生變量的遮蔽。實例(li)如下:

1 var name = "jeri";

2 var name = "tom";

3

4 function glbShadow() {

5 var name = "fun";

6

7 console.log(name); // 輸出:fun

8 }

9

10 glbShadow();

11

12 console.log(name); // 輸出:tom

當(dang)在一(yi)個變量同一(yi)作(zuo)用域內聲(sheng)明了多次(ci)時(shi),后一(yi)次(ci)聲(sheng)明會(hui)生(sheng)效,會(hui)遮蔽以前的(de)聲(sheng)明。

變量聲明的(de)遮蔽很好理解,然而函數(shu)參(can)數(shu)的(de)遮蔽就(jiu)略顯復雜(za)。例如:

1 var shadowed = 0;

2

3 function argShadow(shadowed) {

4 var str = ["Value is",shadowed].join("; ");

5 console.log(str);

6 }

7

8 argShadow(108); // output:Value is 108

9

10 argShadow(); // output:Value is

函數(shu)(shu)argShadow的(de)參數(shu)(shu)shadowed覆(fu)蓋了全局作用域內的(de)同名變量。即使沒有(you)傳(chuan)遞(di)任何參數(shu)(shu),仍(reng)然綁定的(de)是shadowed,并沒有(you)訪(fang)問到全局變量shadowed = 0。

任何情(qing)況下,離得(de)近的變量綁定優先級高。實例(li)如下:

1 var shadowed = 0;

2

3 function varShadow(shadowed) {

4 var shadowed = 123;

5 var str = ["Value is",shadowed].join(" ");

6 console.log(str);

7 }

8

9 varShadow(108); // output:Value is 123

10

11 varShadow(); // output:Value is 123

varShadow(108)打(da)印出來的并不是(shi)(shi)108而是(shi)(shi)123,即使(shi)沒有參數傳入也是(shi)(shi)打(da)印的123,先訪問離得近(jin)的變量(liang)綁定(ding)。

遮蔽變(bian)量同樣發生在(zai)閉包內(nei)部,實例如下(xia):

1 function captureShadow(shadowed) {

2

3 console.log(shadowed); // output:108

4

5 return function(shadowed) {

6

7 console.log(shadowed); // output:2

8 var ret = shadowed + 1;

9 console.log(ret); // output:3

10 }

11 }

12

13 var closureShadow = captureShadow(108);

14

15 closureShadow(2);

在(zai)編寫JavaScript代(dai)碼(ma)時,因(yin)為變(bian)(bian)(bian)量(liang)遮蔽會(hui)使很多變(bian)(bian)(bian)量(liang)綁定(ding)超出我們的控制(zhi),我們應盡量(liang)避免變(bian)(bian)(bian)量(liang)遮蔽,一定(ding)要(yao)注意變(bian)(bian)(bian)量(liang)命名。

典型誤區

下(xia)面是一個(ge)非(fei)常典型(xing)的問題(ti),曾(ceng)經困擾了很(hen)多人,下(xia)面也來(lai)探(tan)討(tao)下(xia)。

1 var test = function() {

2 var ret = [];

3

4 for(var i = 0; i < 5; i++) {

5 ret[i] = function() {

6 return i;

7 }

8 }

9

10 return ret;

11 };

12 var test0 = test()[0]();

13 console.log(test0); // 輸出:5

14

15 var test1 = test()[1]();

16 console.log(test1); //輸出:5

從上面的(de)(de)(de)例子可(ke)知,test這個(ge)(ge)函(han)數(shu)(shu)執(zhi)行(xing)(xing)(xing)(xing)之(zhi)后(hou)返回一個(ge)(ge)函(han)數(shu)(shu)數(shu)(shu)組(zu),表面上看數(shu)(shu)組(zu)內(nei)(nei)的(de)(de)(de)每個(ge)(ge)函(han)數(shu)(shu)都(dou)應該返回自己的(de)(de)(de)索引(yin)(yin)值,然(ran)而(er)(er)并(bing)不是如此。當(dang)外部函(han)數(shu)(shu)執(zhi)行(xing)(xing)(xing)(xing)完(wan)畢后(hou),外部函(han)數(shu)(shu)雖(sui)然(ran)其執(zhi)行(xing)(xing)(xing)(xing)環(huan)境已經銷毀(hui),但閉(bi)包依然(ran)保(bao)留(liu)著對(dui)其中(zhong)變量綁(bang)定的(de)(de)(de)引(yin)(yin)用(yong),仍然(ran)駐(zhu)留(liu)在內(nei)(nei)存(cun)之(zhi)中(zhong)。當(dang)外部函(han)數(shu)(shu)執(zhi)行(xing)(xing)(xing)(xing)完(wan)畢之(zhi)后(hou),才會執(zhi)行(xing)(xing)(xing)(xing)內(nei)(nei)部函(han)數(shu)(shu),而(er)(er)這時(shi)內(nei)(nei)部函(han)數(shu)(shu)捕獲的(de)(de)(de)變量綁(bang)定已經是外部函(han)數(shu)(shu)執(zhi)行(xing)(xing)(xing)(xing)之(zhi)后(hou)的(de)(de)(de)終變量值了,所以這些函(han)數(shu)(shu)都(dou)引(yin)(yin)用(yong)的(de)(de)(de)是同一個(ge)(ge)變量i=5。

下面有個更優(you)雅的例子來(lai)表述這個問(wen)題:

1 for(var i = 0; i < 5; i++) {

2

3 setTimeout(function() {

4 console.log(i);

5 }, 1000);

6 }

7

8 // 每隔1秒輸出一個5

按照我們的(de)推斷,上(shang)例應(ying)該輸出1,2,3,4,5。然而(er),事實上(shang)輸出的(de)是連續(xu)5個5。為什么出現這種詭(gui)異的(de)狀況呢(ni)?其本質上(shang)還(huan)是由閉包(bao)特性造成的(de),閉包(bao)可以(yi)捕獲外部作用域(yu)的(de)變量綁(bang)定。

上面這(zhe)個(ge)函(han)數(shu)片段在執(zhi)行(xing)(xing)(xing)時(shi),其內部函(han)數(shu)和外(wai)部函(han)數(shu)并(bing)不是同(tong)步(bu)執(zhi)行(xing)(xing)(xing)的(de),因(yin)為當調用setTimeout時(shi)會有一(yi)個(ge)延(yan)時(shi)事(shi)件排(pai)入(ru)隊列(lie),等所有同(tong)步(bu)代碼執(zhi)行(xing)(xing)(xing)完(wan)畢后,再依次執(zhi)行(xing)(xing)(xing)隊列(lie)中的(de)延(yan)時(shi)事(shi)件,而(er)這(zhe)個(ge)時(shi)候 i 已經 是5了。

那(nei)怎么(me)解決這個問題呢?我們是(shi)不是(shi)可以在每個循環執(zhi)行(xing)時,給(gei)內部函數傳(chuan)進一個變量(liang)的拷貝,使(shi)其(qi)在每次創建閉(bi)包時,都捕獲一個變量(liang)綁定(ding)。因為(wei)我們每次傳(chuan)參不同,那(nei)么(me)每次捕獲的變量(liang)綁定(ding)也(ye)是(shi)不同的,也(ye)就避免了后輸出5個5的狀況。實例(li)如(ru)下:

1 for(var i = 0; i < 5; i++) {

2

3 (function(j) {

4

5 setTimeout(function() {

6 console.log(j);

7 }, 1000);

8 })(i);

9 }

10

11 // 輸出(chu):0,1,2,3,4

閉包(bao)具有非常強大(da)的(de)功能,函數(shu)內(nei)部(bu)可以引(yin)用(yong)外部(bu)的(de)參數(shu)和變量(liang),但(dan)其參數(shu)和變量(liang)不會被垃圾回(hui)收機制回(hui),常駐內(nei)存,會增(zeng)大(da)內(nei)存使用(yong)量(liang),使用(yong)不當很容易造成(cheng)內(nei)存泄露。但(dan),閉包(bao)也(ye)是(shi)javascript語(yu)言的(de)一大(da)特點,主(zhu)要應用(yong)閉包(bao)場合為:設計私有的(de)方法和變量(liang)。

模擬私有變量

從上文的(de)敘(xu)述(shu)我們(men)知道,變(bian)量的(de)捕獲發生在創建閉包的(de)時候,那(nei)么我們(men)可以把閉包捕獲到的(de)變(bian)量作為私有變(bian)量。實(shi)例如(ru)下:

1 var closureDemo = (function() {

2 var PRIVATE = 0;

3

4 return {

5 inc:function(n) {

6 return PRIVATE += n;

7 },

8 dec:function(n) {

9 return PRIVATE -= n;

10 }

11 };

12 })();

13

14 var testInc = closureDemo.inc(10);

15 //console.log(testInc);

16 // 輸出(chu):10

17

18 var testDec = closureDemo.dec(7);

19 //console.log(testDec);

20 // 輸(shu)出:3

21

22 closureDemo.div = function(n) {

23 return PRIVATE / n;

24 };

25

26 var testDiv = closureDemo.div(3);

27 console.log(testDiv);

28 //輸出:Uncaught ReferenceError: PRIVATE is not defined

自執(zhi)行函(han)數(shu)closureDemo執(zhi)行完(wan)畢之后,自執(zhi)行函(han)數(shu)作(zuo)用域(yu)和PRIVATE隨(sui)之銷(xiao)毀,但PRIVATE仍滯留在內存(cun)中(zhong),也(ye)就是加(jia)入到closureDemo.inc和closureDemo.dec的(de)(de)(de)(de)作(zuo)用域(yu)鏈中(zhong),閉包也(ye)就完(wan)成了變(bian)量的(de)(de)(de)(de)捕獲(huo)。但之后新加(jia)入的(de)(de)(de)(de)closureDemo.div并不能在作(zuo)用域(yu)中(zhong)繼續尋(xun)找到PRIVATE了。因為,函(han)數(shu)只有被調用時才會執(zhi)行函(han)數(shu)里面的(de)(de)(de)(de)代碼,變(bian)量的(de)(de)(de)(de)捕獲(huo)也(ye)只發生在創建閉包時,所以之后新加(jia)入的(de)(de)(de)(de)div方法并不能捕獲(huo)PRIVATE。

創建特權方法

通過閉包(bao)(bao)我們可以(yi)創(chuang)建私(si)(si)(si)有(you)(you)(you)作(zuo)用(yong)域,那么(me)也就可以(yi)創(chuang)建私(si)(si)(si)有(you)(you)(you)變(bian)量和私(si)(si)(si)有(you)(you)(you)函(han)數(shu)。創(chuang)建私(si)(si)(si)有(you)(you)(you)函(han)數(shu)的方式和聲明(ming)(ming)私(si)(si)(si)有(you)(you)(you)變(bian)量方法(fa)一致(zhi),只要在(zai)函(han)數(shu)內(nei)部聲明(ming)(ming)函(han)數(shu)就可以(yi)了。當然,既(ji)然可以(yi)模擬私(si)(si)(si)有(you)(you)(you)變(bian)量和私(si)(si)(si)有(you)(you)(you)函(han)數(shu),我們也可以(yi)利用(yong)閉包(bao)(bao)這個特性(xing),創(chuang)建特權方法(fa)。實例如(ru)下:

1 (function() {

2

3 // 私有變量和私有函數

4 var privateVar = 10;

5

6 function privateFun() {

7 return false;

8 };

9

10 // 構造函數

11 MyObj = function() {

12

13 };

14

15 // 公有/特權方法

16 MyObj.prototype.publicMethod = function() {

17 privateVar ++;

18 return privateFun();

19 }

20 })();

上(shang)面這個(ge)實例創建了(le)(le)一(yi)個(ge)私有作用域,并封裝(zhuang)了(le)(le)一(yi)個(ge)構(gou)造函數(shu)和對應的(de)(de)(de)方法。需要注(zhu)意的(de)(de)(de)是(shi)在上(shang)面的(de)(de)(de)實例中(zhong),在聲明MyObj這個(ge)函數(shu)時,使(shi)用的(de)(de)(de)是(shi)不帶(dai)var的(de)(de)(de)函數(shu)表達(da)式,我(wo)(wo)們(men)希望產生的(de)(de)(de)是(shi)一(yi)個(ge)全局(ju)(ju)函數(shu)而不是(shi)局(ju)(ju)部(bu)的(de)(de)(de),不然(ran)我(wo)(wo)們(men)依(yi)然(ran)在外部(bu)無法訪問。所(suo)以,MyObj就(jiu)成為了(le)(le)一(yi)個(ge)全局(ju)(ju)變量,能夠(gou)在外部(bu)進(jin)行(xing)訪問,我(wo)(wo)們(men)在原(yuan)型上(shang)定義的(de)(de)(de)方法publicMethod也就(jiu)可以使(shi)用,通過這個(ge)方法我(wo)(wo)們(men)也就(jiu)可以訪問私有函數(shu)和私有變量了(le)(le)。

總的(de)來(lai)說,閉包(bao)對我們項目的(de)性能提升(sheng)還是(shi)有(you)(you)一定(ding)貢獻的(de),通(tong)過它可以實(shi)現(xian)一些強(qiang)大的(de)功能。特別(bie)是(shi)其回收不用的(de)局部變量(liang),避(bi)免內存泄漏(lou)。所以有(you)(you)效的(de)掌握還是(shi)很有(you)(you)必要(yao)的(de)。

上一篇:unity3D中的C#編程入門

下一篇:Anddroid App和Java Web服務器間數據交互 之JavaWeb服務器搭建

熱點文章推薦
華(hua)清學員就業榜(bang)單
高薪學(xue)員(yuan)經驗分(fen)享
熱點新聞推薦
前臺專(zhuan)線(xian):010-82525158 企業培訓洽(qia)談專線:010-82525379 院校合作洽談專線:010-82525379 Copyright © 2004-2022 北京華清遠見科技集團有限公司 版權所有 ,,京公海網安備11010802025203號

回到頂部