成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

JavaScript之作用域和閉包

CNZPH / 1412人閱讀

摘要:依然持有對該作用域的引用,而這個(gè)引用就叫作閉包。循環(huán)和閉包正常情況下,我們對這段代碼行為的預(yù)期是分別輸出數(shù)字,每秒一次,每次一個(gè)。

一、作用域

作用域共有兩種主要的工作模型:第一種是最為普遍的,被大多數(shù)編程語言所采用的詞法作用域,另外一種叫作動(dòng)態(tài)作用域;

JavaScript所采用的作用域模式是詞法作用域。

1.詞法作用域

詞法作用域意味著作用域是由書寫代碼時(shí)函數(shù)聲明的位置來決定的。編譯的詞法分析階段基本能夠知道全部標(biāo)識符在哪里以及是如何聲明的,從而能夠預(yù)測在執(zhí)行過程中如何對它們進(jìn)行查找。

JavaScript 中有兩個(gè)機(jī)制可以“欺騙”詞法作用域:

eval(..):可以對一段包含一個(gè)或多個(gè)聲明的“代碼”字符串進(jìn)行演算,并借此來修改已經(jīng)存在的詞法作用域(在運(yùn)行時(shí)) ;

with:通過將一個(gè)對象的引用當(dāng)作作用域來處理,將對象的屬性當(dāng)作作用域中的標(biāo)識符來處理,從而創(chuàng)建了一個(gè)新的詞法作用域(同樣是在運(yùn)行時(shí)) 。

這兩個(gè)機(jī)制的副作用是引擎無法在編譯時(shí)對作用域查找進(jìn)行優(yōu)化,因?yàn)橐嬷荒苤?jǐn)慎地認(rèn)為這樣的優(yōu)化是無效的。使用這其中任何一個(gè)機(jī)制都將導(dǎo)致代碼運(yùn)行變慢。

2.函數(shù)作用域和塊級作用域

函數(shù)作用域: 函數(shù)是 JavaScript 中最常見的作用域單元。本質(zhì)上,聲明在一個(gè)函數(shù)內(nèi)部的變量或函數(shù)會(huì)在所處的作用域中“隱藏”起來,即函數(shù)內(nèi)定于的函數(shù)和變量為該函數(shù)私有;

塊級作用域:

塊作用域指的是變量和函數(shù)不僅可以屬于所處的作用域,也可以屬于某個(gè)代碼塊(通常指 { .. } 內(nèi)部)

ES6前在JavaScript中并不存在塊級作用域( 例外:try/catch 結(jié)構(gòu)在 catch 分句中具有塊作用域);

在 ES6 中引入了 let 關(guān)鍵字( var 關(guān)鍵字的表親) ,用來在任意代碼塊中聲明變量。 if(..) { let a = 2; } 會(huì)聲明一個(gè)劫持了 if 的 { .. } 塊的變量,并且將變量添加到這個(gè)塊中(另外常量定義const也具有塊級作用域)。

3.函數(shù)和變量的提升

(1)、提升

函數(shù)作用域和塊作用域的行為是一樣的,即,某個(gè)作用域內(nèi)的變量,都將附屬于這個(gè)作用域。

引擎會(huì)在解釋 JavaScript 代碼之前首先對其進(jìn)行編譯。編譯階段中的一部分工作就是找到所有的聲明,并用合適的作用域?qū)⑺鼈冴P(guān)聯(lián)起來;

因此包括變量和函數(shù)在內(nèi)的所有聲明都會(huì)在任何代碼被執(zhí)行前首先被處理;

當(dāng)看到 var a = 2; 時(shí),可能會(huì)認(rèn)為這是一個(gè)聲明。但 JavaScript 實(shí)際上會(huì)將其看成兩個(gè)聲明: var a; 和 a = 2; 。第一個(gè)定義聲明是在編譯階段進(jìn)行的。第二個(gè)賦值聲明會(huì)被留在原地等待執(zhí)行階段。

這個(gè)過程就好像變量和函數(shù)聲明從它們在代碼中出現(xiàn)的位置被“移動(dòng)”到了最上面。這個(gè)過程就叫作提升

每個(gè)作用域都會(huì)進(jìn)行提升操作;

(2)、函數(shù)優(yōu)先

函數(shù)聲明和變量聲明都會(huì)被提升。但是函數(shù)會(huì)首先被提升,然后才是變量。

foo(); // 1
var foo;
function foo() {
    console.log( 1 );
}
foo = function() {
    console.log( 2 );
};

會(huì)輸出 1 而不是 2 !這個(gè)代碼片段會(huì)被引擎理解為如下形式:

function foo() {
    console.log( 1 );
}
foo(); // 1
foo = function() {
    console.log( 2 );
};

var foo 盡管出現(xiàn)在 function foo()... 的聲明之前,但它是重復(fù)的聲明(因此被忽略了) ,因?yàn)楹瘮?shù)聲明會(huì)被提升到普通變量之前。

盡管重復(fù)的 var 聲明會(huì)被忽略掉,但出現(xiàn)在后面的函數(shù)聲明還是可以覆蓋前面的。

二、作用域閉包 (1)、理解閉包

當(dāng)函數(shù)可以記住并訪問所在的詞法作用域時(shí),就產(chǎn)生了閉包,即使函數(shù)是在當(dāng)前詞法作用域之外執(zhí)行。

在Javascript語言中,只有函數(shù)內(nèi)部的子函數(shù)才能讀取局部變量,因此可以把閉包簡單理解成"定義在一個(gè)函數(shù)內(nèi)部的函數(shù)"。

在本質(zhì)上,閉包就是將函數(shù)內(nèi)部和函數(shù)外部連接起來的一座橋梁。

(2)、閉包的用途

可以讀取函數(shù)內(nèi)部的變量;

讓變量的值始終保持在內(nèi)存中。

(3)、閉包的產(chǎn)生實(shí)例

可以讀取函數(shù)內(nèi)部的變量

function foo() {
var a = 2;
function bar() {
    console.log( a );
}
return bar;
}
var baz = foo();
baz(); // 2 —— 這就是閉包的效果。

在 foo() 執(zhí)行后,通常會(huì)期待 foo() 的整個(gè)內(nèi)部作用域都被銷毀,因?yàn)槲覀冎酪嬗欣厥掌饔脕磲尫挪辉偈褂玫膬?nèi)存空間;

閉包的“神奇”之處正是可以阻止這件事情的發(fā)生。事實(shí)上內(nèi)部作用域依然存在,因此沒有被回收,因?yàn)?bar() 本身在使用;

拜 bar() 所聲明的位置所賜,它擁有涵蓋 foo() 內(nèi)部作用域的閉包,使得該作用域能夠一直存活,以供 bar() 在之后任何時(shí)間進(jìn)行引用。

bar() 依然持有對該作用域的引用,而這個(gè)引用就叫作閉包。

循環(huán)和閉包:

for (var i=1; i<=5; i++) {
    setTimeout( function timer() {
        console.log( i );
    }, i*1000 );
}

正常情況下,我們對這段代碼行為的預(yù)期是分別輸出數(shù)字 1~5,每秒一次,每次一個(gè)。但實(shí)際上,這段代碼在運(yùn)行時(shí)會(huì)以每秒一次的頻率輸出五次 6:

延遲函數(shù)的回調(diào)會(huì)在循環(huán)結(jié)束時(shí)才執(zhí)行。事實(shí)上,當(dāng)定時(shí)器運(yùn)行時(shí)即使每個(gè)迭代中執(zhí)行的是 setTimeout(.., 0) ,所有的回調(diào)函數(shù)依然是在循環(huán)結(jié)束后才會(huì)被執(zhí)行,因此會(huì)每次輸出一個(gè) 6 出來。

實(shí)際情況是盡管循環(huán)中的五個(gè)函數(shù)是在各個(gè)迭代中分別定義的,但是它們都被封閉在一個(gè)共享的全局作用域中,因此實(shí)際上只有一個(gè) i,即所有函數(shù)共享一個(gè) i 的引用 。

解決方案:使用 IIFE在每次迭代中將本次迭代的i傳入創(chuàng)建的作用域并封閉起來;

for (var i=1; i<=5; i++) {
    (function(j) {
        setTimeout( function timer() {
            console.log( j );
        }, j*1000 );
    })( i );
}

在迭代內(nèi)使用 IIFE 會(huì)為每個(gè)迭代都生成一個(gè)新的作用域,使得延遲函數(shù)的回調(diào)可以將新的作用域封閉在每個(gè)迭代內(nèi)部,每個(gè)迭代中都會(huì)含有一個(gè)具有正確值的變量供我們訪問。

(4)、使用閉包的注意點(diǎn)

由于閉包會(huì)使得函數(shù)中的變量都被保存在內(nèi)存中,內(nèi)存消耗很大,所以不能濫用閉包,否則會(huì)造成網(wǎng)頁的性能問題,在IE中可能導(dǎo)致內(nèi)存泄露。

解決方案:在退出函數(shù)之前,將不使用的局部變量全部刪除。

閉包會(huì)在父函數(shù)外部,改變父函數(shù)內(nèi)部變量的值。所以,如果把父函數(shù)當(dāng)作對象(object)使用,把閉包當(dāng)作它的公用方法(Public Method),把內(nèi)部變量當(dāng)作它的私有屬性(private value),這時(shí)一定要小心,不要隨便改變父函數(shù)內(nèi)部變量的值。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/52382.html

相關(guān)文章

  • JavaScript作用域和閉包

    摘要:依然持有對該作用域的引用,而這個(gè)引用就叫作閉包。循環(huán)和閉包正常情況下,我們對這段代碼行為的預(yù)期是分別輸出數(shù)字,每秒一次,每次一個(gè)。 一、作用域 作用域共有兩種主要的工作模型:第一種是最為普遍的,被大多數(shù)編程語言所采用的詞法作用域,另外一種叫作動(dòng)態(tài)作用域; JavaScript所采用的作用域模式是詞法作用域。 1.詞法作用域 詞法作用域意味著作用域是由書寫代碼時(shí)函數(shù)聲明的位置來決定...

    animabear 評論0 收藏0
  • 【進(jìn)階2-3期】JavaScript深入閉包面試題解

    摘要:閉包面試題解由于作用域鏈機(jī)制的影響,閉包只能取得內(nèi)部函數(shù)的最后一個(gè)值,這引起的一個(gè)副作用就是如果內(nèi)部函數(shù)在一個(gè)循環(huán)中,那么變量的值始終為最后一個(gè)值。 (關(guān)注福利,關(guān)注本公眾號回復(fù)[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實(shí)戰(zhàn)、面試指導(dǎo)) 本周正式開始前端進(jìn)階的第二期,本周的主題是作用域閉包,今天是第8天。 本計(jì)劃一共28期,每期重點(diǎn)攻克一個(gè)面試重難點(diǎn),如果你還不了...

    alanoddsoff 評論0 收藏0
  • javascript作用域和閉包我見

    摘要:查詢是在作用域鏈中,一級級的往上查找該變量的引用。作用域和作用域鏈作用域的概念,應(yīng)該兩張圖幾句話就能解釋吧。這個(gè)建筑代表程序中的嵌套作用域鏈。一層嵌一層的作用域形成了作用域鏈,變量在作用域鏈中的函數(shù)內(nèi)得到了自己的定義。 javascript作用域和閉包之我見 看了《你不知道的JavaScript(上卷)》的第一部分——作用域和閉包,感受頗深,遂寫一篇讀書筆記加深印象。路過的大牛歡迎指點(diǎn)...

    SoapEye 評論0 收藏0
  • JavaScript 闖關(guān)記》作用域和閉包

    摘要:作用域和閉包是最重要的概念之一,想要進(jìn)一步學(xué)習(xí),就必須理解作用域和閉包的工作原理。全局和局部作用域的關(guān)系在函數(shù)體內(nèi),局部變量的優(yōu)先級高于同名的全局變量。作用域鏈的用途,是保證對執(zhí)行環(huán)境有權(quán)訪問的所有變量和函數(shù)的有序訪問。 作用域和閉包是 JavaScript 最重要的概念之一,想要進(jìn)一步學(xué)習(xí) JavaScript,就必須理解 JavaScript 作用域和閉包的工作原理。 作用域 任何...

    Jacendfeng 評論0 收藏0

發(fā)表評論

0條評論

最新活動(dòng)
閱讀需要支付1元查看
<