JS 變量的作用域及閉包

與閉包有關(guān)的概念:變量的作用域和變量的生存周期镐躲。下面本篇文章就來給大家介紹一下JS中變量的作用域及閉包,有一定的參考價(jià)值侍筛,有需要的朋友可以參考一下萤皂,希望對大家有所幫助。

一匣椰、變量的作用域

1裆熙、變量的作用域指變量有效的范圍,與變量定義的位置密切相關(guān)禽笑,作用域是從空間的角度來描述變量的入录,也可以理解為變量的可見性。在某個(gè)范圍內(nèi)變量是可見的佳镜,也就是說僚稿,變量是可用的。

2蟀伸、按照作用域的不同蚀同,變量可分為全局變量和局部變量。

● 全局變量:在全局環(huán)境中聲明的變量

● 局部變量:在函數(shù)中聲明的變量

● 當(dāng)函數(shù)在執(zhí)行時(shí)啊掏,會創(chuàng)建一個(gè)封閉的執(zhí)行期上下文環(huán)境蠢络,函數(shù)內(nèi)部聲明的變量僅可在函數(shù)內(nèi)部使用,外部無法訪問迟蜜,而全局變量則在任何地方都可以使用

3刹孔、在函數(shù)中使用var關(guān)鍵字顯示聲明的變量是局部變量;而沒有用var關(guān)鍵字娜睛,用直接賦值的方式聲明的變量是全局變量

var m=8;

function f1(){

? ? var a1=10;

? ? console.log(m);? //8

}

function f2(){

? ? var a2=20;

? ? console.log(m);? //8

}

f1();

f2();

4髓霞、依賴變量作用域?qū)崿F(xiàn)封裝特性

(1)使用ES6提供的let

(2)通過函數(shù)來創(chuàng)建作用域:

var myObject=(function(){

? ? var _name="yian";? //私有變量

? ? return {

? ? ? ? getName:function(){? //公有方法

? ? ? ? ? ? return _name;

? ? ? ? }

? ? }

})();

console.log(myObject._name);? //undefined

console.log(myObject.getName());? //yian

二、變量的生存周期

1畦戒、對于全局變量來說方库,其生命周期是永久的,除非主動銷毀此全局變量兢交;

2薪捍、對于在函數(shù)內(nèi)用var關(guān)鍵字聲明的局部變量來說,當(dāng)退出函數(shù)時(shí)配喳,這些局部變量即失去它們的價(jià)值,它們會隨著函數(shù)調(diào)用的結(jié)束而被銷毀

3凳干、模仿塊級作用域

(1)用作塊級作用域的匿名函數(shù):將函數(shù)聲明包含在一對圓括號中晴裹,表示它實(shí)際上是一個(gè)函數(shù)表達(dá)式,而緊隨其后的另一對圓括號會立即調(diào)用這個(gè)函數(shù)救赐。

(function(){

? ? //這里是塊級作用域

})();

在匿名函數(shù)中定義的任何變量涧团,都會在執(zhí)行結(jié)束時(shí)被銷毀

(2)先定義一個(gè)函數(shù)只磷,然后調(diào)用它。定義函數(shù)的方式是創(chuàng)建一個(gè)匿名函數(shù)泌绣,并把這個(gè)匿名函數(shù)賦值給變量钮追;而調(diào)用函數(shù)的方式是在函數(shù)的名稱后添加一對圓括號。

var someFunction=function(){

? ? //這里是塊級作用域

};

someFunction();

經(jīng)典問題:

var nodes=document.getElementsByTagName("div");

for(var i= 0,len=nodes.length;i<len;i++){

? ? nodes[i].onclick=function(){

? ? ? ? console.log(i);? //無論點(diǎn)擊哪個(gè)div阿迈,最后彈出的結(jié)果都是5

? ? }

}

解釋:?div節(jié)點(diǎn)的onclick事件是被異步觸發(fā)的元媚,當(dāng)事件被觸發(fā)時(shí),for循環(huán)早已結(jié)束苗沧,此時(shí)i 已經(jīng)是5

解決辦法:

法一:使用ES6中的let

法二:在閉包的幫助下刊棕,把每次循環(huán)的i值都封存起來

var nodes=document.getElementsByTagName("div");

for(var i= 0,len=nodes.length;i<len;i++){

? ? (function(x){

? ? ? ? nodes[i].onclick=function(){

? ? ? ? ? ? console.log(x);

? ? ? ? }

? ? })(i);

}

4、作用:讀取函數(shù)內(nèi)部的變量待逞,并將這些變量的值始終保存在內(nèi)存中

(1)封裝變量:閉包可以把一些不需要暴露在全局的變量封裝為“私有變量”甥角,私有變量包括函數(shù)的參數(shù)、局部變量和在函數(shù)內(nèi)部定義的其他函數(shù)识樱。

例:mult函數(shù)接收number類型的參數(shù)嗤无,并返回這些參數(shù)的乘積、

初始代碼:

var cache={ };

var mult=function(){

? ? var args=Array.prototype.join.call(arguments,",");

? ? if(cache[args]){

? ? ? ? return cache[args];

? ? }

? ? var a=1;

? ? for(var i=0,len=arguments.length;i<len;i++){

? ? ? ? a=a*arguments[i];

? ? }

? ? return cache[args]=a;

};

console.log(mult(1,2,3));? //6

console.log(mult(3,4,5));? //60

使用閉包后:

var mult=(function(){

? ? var cache={ };? //加入緩存機(jī)制怜庸,避免相同參數(shù)的計(jì)算

? ? var calculate=function(){

? ? ? ? var a=1;

? ? ? ? for(var i= 0,len=arguments.length;i<len;i++){

? ? ? ? ? ? a=a*arguments[i];

? ? ? ? }

? ? ? ? return a;

? ? };

? ? return function(){

? ? ? ? var args=Array.prototype.join.call(arguments,",");

? ? ? ? if(args in cache){

? ? ? ? ? ? return cache[args];

? ? ? ? }

? ? ? ? return cache[args]=calculate.apply(null,arguments);

? ? }

})();

補(bǔ)充:in判斷屬性屬于對象

var mycar = {make: "Honda", model: "Accord", year: 1998};

if ( "make" in mycar ){? //屬性名必須是字符串形式翁巍,因?yàn)閙ake不是一個(gè)變量

? ? document.write('true');? // 顯示true

}

else{

? ? document.write('false');

}

(2)延續(xù)局部變量的壽命

例:使用report數(shù)據(jù)上報(bào)時(shí)會丟失30%左右的數(shù)據(jù),原因是img時(shí)report中的局部變量休雌,當(dāng)report函數(shù)調(diào)用結(jié)束后灶壶,img局部變量隨即被銷毀

初始代碼:

var report=function(src){

? ? var image=new Image();

? ? image.src=src;

};

使用閉包后(把img變量用閉包封裝起來):

var report=(function(){

? ? var imgs=[ ];

? ? return function(){

? ? ? ? var image=new Image();

? ? ? ? imgs.push(image);

? ? ? ? image.src=src;

? ? }

})();

5、閉包和面向?qū)ο笤O(shè)計(jì)

閉包寫法:

var extent=function(){

? ? var value=0;

? ? return {

? ? ? ? call:function(){

? ? ? ? ? ? value++;

? ? ? ? ? ? console.log(value);

? ? ? ? }

? ? }

}

var extent=extent();

extent.call();? //1

extent.call();? //2

面向?qū)ο髮懛ㄒ唬?/p>

var extend={

? ? value:0,

? ? call:function(){

? ? ? ? this.value++;

? ? ? ? console.log(this.value);

? ? }

};

extend.call();? //1

extend.call();? //2

面向?qū)ο髮懛ǘ?/p>

var Extend=function(){

? ? this.value=0;

};

Extend.prototype.call=function(){

? ? this.value++;

? ? console.log(this.value);

};

var extend=new Extend();

extend.call();? //1

extend.call();? //2

6杈曲、閉包與內(nèi)存管理

● 局部變量變量應(yīng)該在函數(shù)退出時(shí)被解除引用驰凛,但如果局部變量被封閉在閉包形成的環(huán)境中,那么局部變量就會一直生存下去担扑,即它會常駐內(nèi)存恰响。

● 使用閉包的同時(shí)比較容易形成循環(huán)引用,如果閉包的作用域鏈中保存著一些DOM節(jié)點(diǎn)涌献,這就有可能造成內(nèi)存泄漏胚宦。

● 解決循環(huán)引用帶來的內(nèi)存泄漏問題:把循環(huán)引用中的變量設(shè)為null。(將變量設(shè)置為null以為著切斷變量與它之前引用的值之間的連接燕垃,當(dāng)垃圾收集器下次運(yùn)行時(shí)枢劝,就會刪除這些值并回收它們占用的內(nèi)存)

7、特點(diǎn):

● 函數(shù)嵌套函數(shù)卜壕;

● 在函數(shù)內(nèi)部可引用外部的參數(shù)和變量您旁;

● 參數(shù)和變量不會以垃圾回收機(jī)制回收。

8轴捎、優(yōu)點(diǎn):避免全局變量的污染

9鹤盒、缺點(diǎn):會常駐內(nèi)存蚕脏,增加內(nèi)存的使用量,使用不當(dāng)會造成內(nèi)存泄漏侦锯;閉包會攜帶包含它的函數(shù)的作用域驼鞭,因此會比其他函數(shù)占用更多的內(nèi)存

10、創(chuàng)建閉包

寫法一:

function a() {?

var b=123;?

function c(){

? ? console.log(b+=1);

? ? }?

? ? return c;

}

var d=a();

d();

方式二:

function f1(){

? ? var num=10;? //函數(shù)執(zhí)行完畢尺碰,變量仍然存在

? ? var f2=function(){

? ? ? ? num++;

? ? ? ? console.log(num);? //11

? ? };

? ? return f2;

}

var res=f1();

res();

● 解釋:執(zhí)行f1()后,f1()閉包內(nèi)部的變量會存在挣棕,而閉包內(nèi)部函數(shù)的內(nèi)部變量不會存在,使得JavaScript的垃圾回收機(jī)制不會收回f1()占用的資源葱蝗,因?yàn)閒1()中內(nèi)部函數(shù)的執(zhí)行需要依賴f1()中的變量穴张。

方式三:

function foo(x) {

? ? var tmp = 3;

? ? return function f2(y) {

? ? ? ? alert(x + y + (++tmp));? //17

? ? };

}

var bar = foo(3);? //bar現(xiàn)在是一個(gè)閉包

bar(10);

練習(xí)題:

function f1(){

? var a=1;

? t=function(){

? ? ? a++;

? }

? return function(){

? ? ? console.log(a);

? }

}

var b=f1();? //返回值為一個(gè)匿名函數(shù)

b();? //1

t();

b();? //2

聲明變量,若變量名稱相同两曼,就近原則:

var name="g";

function out(){

? ? var name="loc";

? ? function foo(){

? ? ? ? console.log(name);

? ? }

? ? foo();

}

out();? //name=loc

補(bǔ)充知識點(diǎn):

1皂甘、JS中有哪些垃圾回收機(jī)制?

(1)引用計(jì)數(shù):跟蹤記錄每個(gè)值被使用的次數(shù)悼凑。

● 當(dāng)聲明一個(gè)變量并將一個(gè)引用類型賦值給該變量時(shí)偿枕,該值的引用次數(shù)加1;

● 若該變量的值變?yōu)榱硪粋€(gè)户辫,則該值引用次數(shù)減1渐夸;

● 若該值引用次數(shù)為0時(shí),說明變量沒有在使用渔欢,此值無法訪問墓塌;

● 因此,可以將它占用的空間回收奥额,垃圾回收機(jī)制會在運(yùn)行時(shí)清理引用次數(shù)為0 的值所占用的空間苫幢。

● 在低版的IE中會發(fā)生內(nèi)存泄漏,很多時(shí)候就是因?yàn)樗捎靡糜?jì)數(shù)得到方式進(jìn)行垃圾回收(如果兩個(gè)對象之間形成了循環(huán)引用垫挨,那么這兩個(gè)對象都無法被回收)韩肝。

(2)標(biāo)記清除:最常見的垃圾回收方式

● 當(dāng)變量進(jìn)入執(zhí)行環(huán)境時(shí),垃圾回收器將其標(biāo)為“進(jìn)入環(huán)境”九榔,離開時(shí)標(biāo)記為“離開環(huán)境”轧坎;

● 垃圾回收機(jī)制在運(yùn)行時(shí)給存儲在內(nèi)存中的所有變量加上標(biāo)記陋桂;

● 去掉環(huán)境中的變量及被環(huán)境中變量所引用的變量(閉包)的標(biāo)記;

● 完成這些后仍存在的標(biāo)記就是要刪除的變量瓶蝴。

2郎汪、哪些操作會造成內(nèi)存泄漏竹握?

● 內(nèi)存泄漏:指不再擁有或需要任何對象(數(shù)據(jù))之后抖格,它們?nèi)匀淮嬖谟趦?nèi)存中冕房。

● 垃圾回收器定期掃描對象,并計(jì)算引用了每個(gè)對象的其他對象的數(shù)量牢屋。如果一個(gè)對象的引用數(shù)量為0(沒有其他對象引用過該對象)且预,或?qū)υ搶ο蟮奈ㄒ灰檬茄h(huán)的,那么該對象占用的內(nèi)存立即被回收烙无。

● 如果setTimeout的第一個(gè)參數(shù)使用字符串而非函數(shù)锋谐,會造成內(nèi)存泄漏。

● 閉包截酷、控制臺日志涮拗、循環(huán)(在兩個(gè)對象彼此引用且彼此保留時(shí),就會產(chǎn)生一個(gè)循環(huán))等會造成內(nèi)存泄漏迂苛。

更多前端開發(fā)知識三热,請查閱 HTML中文網(wǎng) !三幻!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末就漾,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子念搬,更是在濱河造成了極大的恐慌抑堡,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件朗徊,死亡現(xiàn)場離奇詭異首妖,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)爷恳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進(jìn)店門有缆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人温亲,你說我怎么就攤上這事棚壁。” “怎么了铸豁?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵灌曙,是天一觀的道長。 經(jīng)常有香客問我节芥,道長在刺,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任头镊,我火速辦了婚禮蚣驼,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘相艇。我一直安慰自己颖杏,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布坛芽。 她就那樣靜靜地躺著留储,像睡著了一般翼抠。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上获讳,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天阴颖,我揣著相機(jī)與錄音,去河邊找鬼丐膝。 笑死量愧,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的帅矗。 我是一名探鬼主播偎肃,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼浑此!你這毒婦竟也來了累颂?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤尤勋,失蹤者是張志新(化名)和其女友劉穎喘落,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體最冰,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡瘦棋,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了暖哨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片赌朋。...
    茶點(diǎn)故事閱讀 40,030評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖篇裁,靈堂內(nèi)的尸體忽然破棺而出沛慢,到底是詐尸還是另有隱情,我是刑警寧澤达布,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布团甲,位于F島的核電站,受9級特大地震影響黍聂,放射性物質(zhì)發(fā)生泄漏躺苦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一产还、第九天 我趴在偏房一處隱蔽的房頂上張望匹厘。 院中可真熱鬧,春花似錦脐区、人聲如沸愈诚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽炕柔。三九已至酌泰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間汗唱,已是汗流浹背宫莱。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工丈攒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留哩罪,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓巡验,卻偏偏與公主長得像际插,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子显设,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評論 2 355

推薦閱讀更多精彩內(nèi)容