国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

作用域與閉包

shery / 1822人閱讀

摘要:依然持有對(duì)該作用域的引用,而這個(gè)引用就叫作閉包。無論通過何種手段將內(nèi)部函數(shù)傳遞到所在的詞法作用域以外,它都會(huì)持有對(duì)原始定義作用域的引用,無論在何處執(zhí)行這個(gè)函數(shù)都會(huì)使用閉包。

因?yàn)樽罱?xiàng)目比較少,閑來覺得需要學(xué)習(xí)《你不知道的JavaScript》;跟大家分享一下;

什么是作用域

需要一套設(shè)計(jì)良好的規(guī)則來存儲(chǔ)變量,并且之后可以方便地找到這些變量。這套規(guī)
則被稱為作用域

執(zhí)行 var a = 2 發(fā)生了什么

1.var a: 編譯器會(huì)詢問作用域是否存在變量a;如果是,編譯器會(huì)忽略該聲明,繼續(xù)進(jìn)行編譯。否則它會(huì)要求作用域在當(dāng)前作用域的集合中聲明一個(gè)新的變量,并命名為a;接下來編譯器會(huì)為引擎生成運(yùn)行時(shí)所需的代碼,這些代碼被用來處理a = 2這個(gè)賦值操作。

2.引擎運(yùn)行時(shí)會(huì)首先詢問作用域,在當(dāng)前的作用域集合中是否存在一個(gè)叫作a的變量。如果否,引擎就會(huì)
使用這個(gè)變量;如果不是,引擎會(huì)繼續(xù)查找該變量如果引擎最終找到了a變量,就會(huì)將2賦值給它。否則引擎就會(huì)舉手示意并拋出一個(gè)異常!

RHS查詢 與 LHS查詢

RHS查詢:簡單地查找某個(gè)變量的值
LHS查詢:試圖找到變量的容器本身,從而可以對(duì)其賦值

在概念上最好將其理解為“賦值操作的目標(biāo)是誰(LHS)”以及“誰是賦值操作的源頭(RHS)”。
LHS:對(duì)哪個(gè) 賦值 就對(duì)哪個(gè)進(jìn)行LHS引用,可以理解為賦值操作的目標(biāo)。
RHS:需要 獲取 哪個(gè)變量的值,就對(duì)哪個(gè)變量的值進(jìn)行RHS引用,理解為賦值操作的源頭。

作用域嵌套

當(dāng)一個(gè)塊或函數(shù)嵌套在另一個(gè)塊或函數(shù)中時(shí),就發(fā)生了作用域的嵌套。因此,在當(dāng)前作用域中無法
找到某個(gè)變量時(shí),引擎就會(huì)在外層嵌套的作用域中繼續(xù)查找,直到找到該變量,或抵達(dá)最外層的作用域(也就是全局作用域)為止。

遍歷嵌套作用域鏈的規(guī)則很簡單:引擎從當(dāng)前的執(zhí)行作用域開始查找變量,如果找不到,就向上一
級(jí)繼續(xù)查找。當(dāng)?shù)诌_(dá)最外層的全局作用域時(shí),無論找到還是沒找到,查找過程都會(huì)停止。

嚴(yán)格模式下的 ReferenceError 與 TypeError

如果RHS查詢在所有嵌套的作用域中遍尋不到所需的變量,引擎就會(huì)拋出ReferenceError異常

在嚴(yán)格模式中LHS查詢失敗時(shí),并不會(huì)創(chuàng)建并返回一個(gè)全局變量,引擎會(huì)拋出同RHS查詢失敗時(shí)類似的ReferenceError異常。

如果RHS查詢找到了一個(gè)變量,但是你嘗試對(duì)這個(gè)變量的值進(jìn)行不合理的賦值,那么引擎會(huì)

拋出另外一種類型的異常,叫作TypeError。

ReferenceError同作用域判別失敗相關(guān),而TypeError則代表作用域判別成功了,但是對(duì)結(jié)果的操作

是非法或不合理的。

遮蔽效應(yīng)

在多層的嵌套作用域中可以定義同名的標(biāo)識(shí)符,這叫作“遮蔽效應(yīng)”(內(nèi)部的標(biāo)識(shí)符“遮蔽”了外部的標(biāo)識(shí)符)。作用域查找始終從運(yùn)行時(shí)所處的最內(nèi)部作用域開始,逐級(jí)向外或者說向上進(jìn)行,直到遇見第一個(gè)匹配的標(biāo)識(shí)符為止。

全局變量會(huì)自動(dòng)成為全局對(duì)象(比如瀏覽器中的window對(duì)象)的屬性,所以如果要逃避遮蔽效應(yīng)
可以通過 window對(duì)象

window.a  //得到的是全局定義的a變量;
全局命名空間

庫通常會(huì)在全局作用域中聲明一個(gè)名字足夠獨(dú)特的變量,通常是一個(gè)對(duì)象。這個(gè)對(duì)象被用作
庫的命名空間,所有需要暴露給外界的功能都會(huì)成為這個(gè)對(duì)象(命名空間)的屬性,而不是將自己
的標(biāo)識(shí)符暴漏在頂級(jí)的詞法作用域中。

函數(shù)作用域的問題

我們已經(jīng)知道,在任意代碼片段外部添加包裝函數(shù),可以將內(nèi)部的變量和函數(shù)定義“隱藏”起來,外
部作用域無法訪問包裝函數(shù)內(nèi)部的任何內(nèi)容。
雖然這種技術(shù)可以解決一些問題,但是它并不理想,因?yàn)闀?huì)導(dǎo)致一些額外的問題。首先,必須聲明
一個(gè)具名函數(shù)foo(),意味著foo這個(gè)名稱本身“污染”了所在作用域(在這個(gè)例子中是全局作用域)。
其次,必須顯式地通過函數(shù)名(foo())調(diào)用這個(gè)函數(shù)才能運(yùn)行其中的代碼。

更加理想的方式

var a = 2;
(function foo(){ // <-- 添加這一行
    var a = 3;
    console.log( a ); // 3
})(); // <-- 以及這一行
console.log( a ); // 2

以(function...而不僅是以function...開始。函數(shù)會(huì)被當(dāng)作函數(shù)表達(dá)式而不是一個(gè)標(biāo)準(zhǔn)的函數(shù)聲明 來處理。

函數(shù)聲明和函數(shù)表達(dá)式之間最重要的區(qū)別是它們的名稱標(biāo)識(shí)符將會(huì)綁定在何處。foo被綁定在函數(shù)表達(dá)式自身的函數(shù)中而不是所在作用域中。

換句話說,(function foo(){ .. })作為函數(shù)表達(dá)式意味著foo只能在..所代表的位置中被訪問,外

部作用域則不行。foo變量名被隱藏在自身中意味著不會(huì)非必要地污染外部作用域。

很多人都更喜歡另一個(gè)改進(jìn)的形式:(function(){ .. }())。這兩種形式在功能上是一致的。選擇哪個(gè)全憑個(gè)人喜好.

塊 作用域
for (var i=0; i<10; i++) {
console.log( i );
}

我們在for循環(huán)的頭部直接定義了變量i,通常是因?yàn)橹幌朐趂or循環(huán)內(nèi)部的上下文中使用i,而忽
略了i會(huì)被綁定在外部作用域(函數(shù)或全局)中的事實(shí)。

JavaScript的ES3規(guī)范中規(guī)定try/catch的catch分句會(huì)創(chuàng)建一個(gè)塊作用域,其中聲明的變量僅在catch內(nèi)部有效。

try {
undefined(); // 執(zhí)行一個(gè)非法操作來強(qiáng)制制造一個(gè)異常
}c
atch (err) {
console.log( err ); // 能夠正常執(zhí)行!
} c
onsole.log( err ); // ReferenceError: err not found

ES6改變了現(xiàn)狀,引入了新的let關(guān)鍵字,提供了除var以外的另一種變量聲明方式
let關(guān)鍵字可以將變量綁定到所在的任意作用域中(通常是{ .. }內(nèi)部)。只要聲明是有效的,在聲明中的任意位置都可以使用{ .. }括號(hào)來為let創(chuàng)建一個(gè)用于綁定的塊。

為變量顯式聲明塊作用域,并對(duì)變量進(jìn)行本地綁定是非常有用的工具,可以讓引擎清楚地知道沒有必要繼續(xù)保存那些變量(當(dāng)塊的變量沒有被引用時(shí)就銷毀);

const

除了let以外,ES6還引入了const,同樣可以用來創(chuàng)建塊作用域變量,但其值是固定的(常量)。之后
任何試圖修改值的操作都會(huì)引起錯(cuò)誤。

提升

解析兩個(gè)輸出

a = 2;
var a;
console.log( a );//2
console.log( a ); //undefined
var a = 2;

編譯器順序

當(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í)際是按照以下流程處理的:

var a;
console.log( a );
a = 2;

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

函數(shù)聲明會(huì)優(yōu)先于變量聲明

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

相當(dāng)于

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

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

詞法作用域就是定義在詞法階段的作用域。換句話說,詞法作用域是由你在寫代碼時(shí)將 變量和塊作用域?qū)懺谀睦飦頉Q定的

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

我們將bar()函數(shù)本身當(dāng)作一個(gè)值類型進(jìn)行傳遞。

函數(shù)bar()的詞法作用域能夠訪問foo()的內(nèi)部作用域。

在foo()執(zhí)行后,其返回值(也就是內(nèi)部的bar()函數(shù))賦值給變量baz并調(diào)用baz(),實(shí)際上只是通過不同的標(biāo)識(shí)符引用調(diào)用了內(nèi)部的函數(shù)bar()。

因?yàn)槲覀冎酪嬗欣厥掌饔脕磲尫挪辉偈褂玫膬?nèi)存空間。由于看上去foo()的內(nèi)容不會(huì)再被使用,所以很自然地會(huì)考慮對(duì)其進(jìn)行回收。在foo()執(zhí)行后,通常會(huì)期待foo()的整個(gè)內(nèi)部作用域都被銷毀。

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

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

bar()依然持有對(duì)該作用域的引用,而這個(gè)引用就叫作閉包。
無論通過何種手段將內(nèi)部函數(shù)傳遞到所在的詞法作用域以外,它都會(huì)持有對(duì)原始定義作用域的引
用,無論在何處執(zhí)行這個(gè)函數(shù)都會(huì)使用閉包。

本質(zhì)上無論何時(shí)何地,如果將函數(shù)(訪問它們各自的詞法作用域)當(dāng)作第一級(jí)的值類型并到處傳遞,你就會(huì)看到閉包在這些函數(shù)中的應(yīng)用。只要使用了回調(diào)函數(shù),實(shí)際上就是在使用閉包!

一個(gè)神奇的例子
for (var i=1; i<=5; i++) {
    setTimeout( function timer() {
        console.log( i );
    }, i*1000 );
}

//每秒出現(xiàn)一個(gè)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è)迭代在運(yùn)行時(shí)都會(huì)給自己“捕獲”一個(gè)i的副本。但是根據(jù)作用域的工作原理,實(shí)際情況是盡管循環(huán)中的五個(gè)函數(shù)是在各個(gè)迭代中分別定義的,但是它們都被封閉在一個(gè)共享的全局作用域中,因此實(shí)際上只有一個(gè)i。

改進(jìn)寫法

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

//每秒從1到6依次輸出

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

細(xì)說:

for (var i = 0; i <= 5; i++) {
       console.log(i)
}

我們都知道上面的代碼可以輸出1到6;因此可以明白

for (var i=1; i<=5; i++) {
    (function(j) {  })( i );
}

//這一層,是會(huì)不斷的獲取到正確的i值;

然后,由于延遲函數(shù)的回調(diào) 使用了閉包;每次閉包都會(huì)保存IIFE 的有正確值的作用域;

閉包的應(yīng)用-- 模塊

直接抄代碼看:

function CoolModule() {
    var something = "cool";
    var another = [1, 2, 3];
    function doSomething() {
        console.log( something );
    } 
    function doAnother() {
        console.log( another.join( " ! " ) );
    } 
    return {
        doSomething: doSomething,
        doAnother: doAnother
    };
} 
var foo = CoolModule();
foo.doSomething(); // cool
foo.doAnother(); // 1 ! 2 ! 3

很好理解,coolmodule()返回一個(gè)對(duì)象,這個(gè)對(duì)象通過不同的標(biāo)識(shí)符引用調(diào)用了內(nèi)部的函數(shù);這些函數(shù)就是coolmodule的閉包,具有訪問coolmodule作用域的能力;

除了返回一個(gè)對(duì)象,還可以返回一個(gè)函數(shù)

從模塊中返回一個(gè)實(shí)際的對(duì)象并不是必須的,也可以直接返回一個(gè)內(nèi)部函數(shù)。jQuery就是
一個(gè)很好的例子。jQuery和$標(biāo)識(shí)符就是jQuery模塊的公共API,但它們本身都是函數(shù)(由于函數(shù)也是對(duì)象,它們本身也可以擁有屬性)。因此,jq的方法有兩種,通過$.xxx() 運(yùn)行的是jq的屬性方法;通過$() 運(yùn)行的是jq的函數(shù)方法;

閉包的應(yīng)用-- 實(shí)現(xiàn)模塊

模塊模式需要具備兩個(gè)必要條件。

必須有外部的封閉函數(shù),該函數(shù)必須至少被調(diào)用一次(每次調(diào)用都會(huì)創(chuàng)建一個(gè)新的模塊實(shí)例)。

封閉函數(shù)必須返回至少一個(gè)內(nèi)部函數(shù),這樣內(nèi)部函數(shù)才能在私有作用域中形成閉包,并且可以訪問或者修改私有的狀態(tài)。

單例模式

var foo = (function CoolModule() {
                var something = "cool";
                var another = [1, 2, 3];
                function doSomething() {
                console.log( something );
                } f
                unction doAnother() {
                console.log( another.join( " ! " ) );
                }return {
                doSomething: doSomething,
                doAnother: doAnother
                };
          })();
foo.doSomething(); // cool
foo.doAnother(); // 1 ! 2 ! 3

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/89648.html

相關(guān)文章

  • Javascript重溫OOP之作用域與閉包

    摘要:的變量作用域是基于其特有的作用域鏈的。需要注意的是,用創(chuàng)建的函數(shù),其作用域指向全局作用域。所以,有另一種說法認(rèn)為閉包是由函數(shù)和與其相關(guān)的引用環(huán)境組合而成的實(shí)體。 作用域 定義 在編程語言中,作用域控制著變量與參數(shù)的可見性及生命周期,它能減少名稱沖突,而且提供了自動(dòng)內(nèi)存管理 --javascript 語言精粹 我理解的是,一個(gè)變量、函數(shù)或者成員可以在代碼中訪問到的范圍。 js的變量作...

    JessYanCoding 評(píng)論0 收藏0
  • Javascript中的作用域與閉包

    摘要:作用域分為詞法作用域和動(dòng)態(tài)作用域。這樣就形成了一個(gè)鏈?zhǔn)降淖饔糜?。一般情況下,當(dāng)函數(shù)執(zhí)行完畢時(shí),里面的變量會(huì)被自動(dòng)銷毀。而能夠訪問到這個(gè)在的編譯階段就已經(jīng)定型了詞法作用域。 什么是作用域?在當(dāng)前運(yùn)行環(huán)境下,可以訪問的變量或函數(shù)的范圍。作用域分為詞法作用域和動(dòng)態(tài)作用域。詞法作用域是在js代碼編譯階段就確定下來的; 對(duì)應(yīng)的,with和eval語句會(huì)產(chǎn)生動(dòng)態(tài)作用域。 會(huì)產(chǎn)生新的作用域的情況: ...

    tianren124 評(píng)論0 收藏0
  • Js基礎(chǔ)知識(shí)(三) - 作用域與閉包

    摘要:是詞法作用域工作模式。使用可以將變量綁定在所在的任意作用域中通常是內(nèi)部,也就是說為其聲明的變量隱式的劫持了所在的塊級(jí)作用域。 作用域與閉包 如何用js創(chuàng)建10個(gè)button標(biāo)簽,點(diǎn)擊每個(gè)按鈕時(shí)打印按鈕對(duì)應(yīng)的序號(hào)? 看到上述問題,如果你能看出來這個(gè)問題實(shí)質(zhì)上是考對(duì)作用域的理解,那么恭喜你,這篇文章你可以不用看了,說明你對(duì)作用域已經(jīng)理解的很透徹了,但是如果你看不出來這是一道考作用域的題目,...

    lemanli 評(píng)論0 收藏0
  • Js基礎(chǔ)知識(shí)(三) - 作用域與閉包

    摘要:是詞法作用域工作模式。使用可以將變量綁定在所在的任意作用域中通常是內(nèi)部,也就是說為其聲明的變量隱式的劫持了所在的塊級(jí)作用域。 作用域與閉包 如何用js創(chuàng)建10個(gè)button標(biāo)簽,點(diǎn)擊每個(gè)按鈕時(shí)打印按鈕對(duì)應(yīng)的序號(hào)? 看到上述問題,如果你能看出來這個(gè)問題實(shí)質(zhì)上是考對(duì)作用域的理解,那么恭喜你,這篇文章你可以不用看了,說明你對(duì)作用域已經(jīng)理解的很透徹了,但是如果你看不出來這是一道考作用域的題目,...

    XFLY 評(píng)論0 收藏0
  • Js基礎(chǔ)知識(shí)(三) - 作用域與閉包

    摘要:是詞法作用域工作模式。使用可以將變量綁定在所在的任意作用域中通常是內(nèi)部,也就是說為其聲明的變量隱式的劫持了所在的塊級(jí)作用域。 作用域與閉包 如何用js創(chuàng)建10個(gè)button標(biāo)簽,點(diǎn)擊每個(gè)按鈕時(shí)打印按鈕對(duì)應(yīng)的序號(hào)? 看到上述問題,如果你能看出來這個(gè)問題實(shí)質(zhì)上是考對(duì)作用域的理解,那么恭喜你,這篇文章你可以不用看了,說明你對(duì)作用域已經(jīng)理解的很透徹了,但是如果你看不出來這是一道考作用域的題目,...

    tanglijun 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

shery

|高級(jí)講師

TA的文章

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