摘要:理解作用域在引擎看來(lái)是兩個(gè)完全不同的聲明。在循環(huán)中使用閉包閉包是函數(shù)和聲明該函數(shù)的詞法環(huán)境的組合。回到我們上面說(shuō)的在自己定義的作用域以外的地方執(zhí)行,這里聲明的是全局變量,使用全局變量不構(gòu)成閉包。
第一章:作用域是什么
程序中變量存儲(chǔ)在哪里,需要是怎么找到它,這就需要設(shè)計(jì)一套存儲(chǔ)以及能方便的找到它的規(guī)則,這個(gè)規(guī)則就是作用域
編譯原理JavaScript 是一門(mén)編譯語(yǔ)言,它與傳統(tǒng)編譯語(yǔ)言不同,但編譯步驟又非常相似;它不是提前編譯的,編譯結(jié)果也不能在分布式系統(tǒng)中進(jìn)行移植。
傳統(tǒng)編譯步驟分詞 / 詞法分析
將由字符組成的字符串分解成有意義的代碼,例如:var a = 2;通常會(huì)被分解為var、a、=、;這些詞法單元,判斷a是獨(dú)立的詞法單元還是其他詞法單元一部分時(shí),如果調(diào)用的是有狀態(tài)的解析規(guī)則那這個(gè)過(guò)程就是詞法分析。
解析 / 語(yǔ)法分析
將詞法單元流(數(shù)組)轉(zhuǎn)換成一個(gè)由元素逐級(jí)嵌套所組成的代表程序語(yǔ)法結(jié)構(gòu)的樹(shù),叫做“抽象語(yǔ)法樹(shù)”(Abstract Syntax Tree,AST)
代碼生成
將 AST 轉(zhuǎn)換為可執(zhí)行代碼的過(guò)程被稱(chēng)為代碼生成。簡(jiǎn)單說(shuō)就是:將var a = 2;的 AST 轉(zhuǎn)化為一組機(jī)器指令,用來(lái)創(chuàng)建一個(gè)a的變量(包括分配內(nèi)存),并將一個(gè)值存儲(chǔ)在a中。
var a = 2;在 JavaScript 引擎看來(lái)是兩個(gè)完全不同的聲明。
遇到var a首先編譯器會(huì)向作用域詢問(wèn)是否有a變量存在這一作用域,如果有就忽略,沒(méi)有就創(chuàng)建a。
接下來(lái)處理a = 2這個(gè)賦值操作,先向當(dāng)前作用域詢問(wèn)是否有這個(gè)變量,如果有就使用這個(gè)變量,并賦值;如果沒(méi)有,繼續(xù)找。
LHS查詢 RHS查詢
LHS 賦值操作的目標(biāo)是誰(shuí);RHS 誰(shuí)是賦值操作的源頭(我要查找×××,并把它給我);簡(jiǎn)單說(shuō)如果查找的目的是對(duì)變量進(jìn)行賦值,會(huì)使用 LHS 查詢,如果查找的目的是為了獲取變量值,會(huì)使用 RHS 查詢。
var a = foo(a){...} 這里的聲明,a = ...并不會(huì)做 LHS 查詢。
function foo(a){ var b = a; return a + b; } var c = foo(2)
LHS c = ... a = 2(隱示變量分配) b = ... RHS foo(...) = a a ... ... b第二章:詞法作用域
詞法作用域是在寫(xiě)代碼時(shí),將變量和塊級(jí)作用域?qū)懺谀睦飦?lái)決定的
詞法階段function foo(a){ var b = a * 2; function bar(c){ console.log(a,b,c); //2,4,12 } bar( b * 3); } foo(2);
全局作用域下包含foo
foo作用域下包含a、b、bar
bar作用域下包含c
對(duì)b、c的 RHS 查詢是在上一級(jí)作用域中完成的
eval()接受一個(gè)字符串作為參數(shù),把它當(dāng)做代碼來(lái)執(zhí)行,在哪個(gè)作用域下調(diào)用它,它的作用域就在哪邊
function foo(str,a){ eval(str); console.log(a,b); //2,3 } var b = 2; foo("var b = 3;",2)
with可以簡(jiǎn)化單調(diào)重復(fù)的賦值操作
如
var obj ={ a = 1, b = 2, c = 3 }; //單調(diào)重復(fù)的賦值 obj.a = 4; obj.b = 5; obj.c = 6; //用with簡(jiǎn)化 with(obj){ a = 7; b = 8; c = 9; }
function foo(obj){ with(obj){ //相當(dāng)于 obj.a = 2,區(qū)別 with 創(chuàng)建了全局變量 a a = 2; } } var o1 = { a:3; } var o2 ={ b:3; } foo(o1) console.log(o1.a) //2 foo(o2) console.log(o2.a) //undefined console.log(a) //2
eval()、with實(shí)際工作中不推薦使用,會(huì)影響性能。
第三章:函數(shù)作用域和塊作用域 函數(shù)中的作用域函數(shù)內(nèi)部可以訪問(wèn)函數(shù)外部的變量,函數(shù)外部不可以訪問(wèn)函數(shù)內(nèi)部變量
函數(shù)作用域函數(shù)的名稱(chēng)也會(huì)污染全局作用域,可以用匿名函數(shù)+立即執(zhí)行函數(shù)來(lái)實(shí)現(xiàn)
立即執(zhí)行函數(shù)有一個(gè)bug,上一行表達(dá)式必須要分號(hào)結(jié)尾,省略會(huì)報(bào)錯(cuò)
var a = 2; (function(global){ var a = 3; console.log(a); //3 console.log(global.a) //2 })(window)
var a = 2; (function(){ var a = 3; console.log(a); //3 }())作用域閉包
1. 在自己定義的作用域以外的地方執(zhí)行
2. 使用回調(diào)函數(shù)也是閉包
function foo(){ var a = 2 function bar(){ console.log(a) } return bar } var baz = foo() baz() //2 這里就用了閉包
bar是在foo里面定義的,所以它是在foo作用域下,但調(diào)用它的地方是在foo作用域外,就這構(gòu)成了閉包。
在來(lái)看一個(gè)
function foo(){ var a = 2 function bar(){ console.log(a) } baz(bar) } function baz(fn){ fn() //2,在這里執(zhí)行了bar,這里構(gòu)成了閉包 } foo()
還有
var fn function foo(){ var a =2 function bar(){ console.log(a) } fn = baz } function baz(){ fn() } foo() baz() //2,構(gòu)成了閉包,這里執(zhí)行了 bar,構(gòu)成了閉包。
在循環(huán)中使用閉包
閉包是函數(shù)和聲明該函數(shù)的詞法環(huán)境的組合。
回到我們上面說(shuō)的:在自己定義的作用域以外的地方執(zhí)行,這里聲明的i是全局變量,使用全局變量不構(gòu)成閉包。
for(var i = 1; i <= 3; i++){ setTimeout(function(){ console.log(i) //打印出 3 個(gè) 4,這里沒(méi)有閉包 },1000) }
如果要寫(xiě)成閉包的樣子,必須要在外面用函數(shù)包裹一層,并調(diào)用它,才能形成閉包
function xxx(){ for(var i = 1; i <= 3; i++){ setTimeout(function(){ console.log(i) //打印出 3 個(gè) 4,這是一個(gè)閉包沒(méi)有立即執(zhí)行 },1000) } } xxx()
優(yōu)化1:這里setTimeout里面的i和立即執(zhí)行函數(shù)的形參構(gòu)成閉包,阻隔了與for循環(huán)的i形成閉包。
function xxx(){ for(var i = 1; i <= 3; i++){ (function(i){ //這個(gè)形參 i 和外面的 i 不是同一個(gè)變量 setTimeout(function(){ console.log(i) //1,2,3,用立即執(zhí)行函數(shù), },1000) })(i) } } xxx()
優(yōu)化2:用let聲明
function xxx(){ for(let i = 1; i <= 3; i++){ setTimeout(function(){ console.log(i) //1,2,3,用 let 聲明變量,在循環(huán)的過(guò)程中不止一次的聲明 },1000) } } xxx()
模塊中閉包
模塊中閉包需要具備兩個(gè)條件:
必須有外部的封閉函數(shù),該函數(shù)至少被調(diào)用一次
封閉函數(shù)至少返回一個(gè)內(nèi)部函數(shù)(可以用 return 或 window)
例:
var foo = function(){ function a(){ console.log(1) } function b(){ console.log(2) } return { a:a, b:b } } var c = foo() c.a() //1
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/96175.html
摘要:詞法作用域的查找規(guī)則是閉包的一部分。因此的確同閉包息息相關(guān),即使本身并不會(huì)真的使用閉包。而上面的創(chuàng)建一個(gè)閉包,本質(zhì)上這是將一個(gè)塊轉(zhuǎn)換成一個(gè)可以被關(guān)閉的作用域。結(jié)合塊級(jí)作用域與閉包模塊這個(gè)模式在中被稱(chēng)為模塊。 你不知道的JS(上卷)筆記 你不知道的 JavaScript JavaScript 既是一門(mén)充滿吸引力、簡(jiǎn)單易用的語(yǔ)言,又是一門(mén)具有許多復(fù)雜微妙技術(shù)的語(yǔ)言,即使是經(jīng)驗(yàn)豐富的 Jav...
摘要:的分句會(huì)創(chuàng)建一個(gè)塊作用域,其聲明的變量?jī)H在中有效。而閉包的神奇作用是阻止此事發(fā)生。依然持有對(duì)該作用域的引用,而這個(gè)引用就叫做閉包。當(dāng)然,無(wú)論使用何種方式對(duì)函數(shù)類(lèi)型的值進(jìn)行傳遞,當(dāng)函數(shù)在別處被調(diào)用時(shí)都可以觀察到閉包。 date: 16.12.8 Thursday 第一章 作用域是什么 LHS:賦值操作的目標(biāo)是誰(shuí)? 比如: a = 2; RHS:誰(shuí)是賦值操作的源頭? 比如: conso...
摘要:如果是聲明中的第一個(gè)詞,那么就是一個(gè)函數(shù)聲明,否則就是一個(gè)函數(shù)表達(dá)式。給函數(shù)表達(dá)式指定一個(gè)函數(shù)名可以有效的解決以上問(wèn)題。始終給函數(shù)表達(dá)式命名是一個(gè)最佳實(shí)踐。也有開(kāi)發(fā)者干脆關(guān)閉了靜態(tài)檢查工具對(duì)重復(fù)變量名的檢查。 你不知道的JS(上卷)筆記 你不知道的 JavaScript JavaScript 既是一門(mén)充滿吸引力、簡(jiǎn)單易用的語(yǔ)言,又是一門(mén)具有許多復(fù)雜微妙技術(shù)的語(yǔ)言,即使是經(jīng)驗(yàn)豐富的 Ja...
摘要:吐槽一下,閉包這個(gè)詞的翻譯真是有很大的誤解性啊要說(shuō)閉包,要先說(shuō)下詞法作用域。閉包兩個(gè)作用通過(guò)閉包,在外部環(huán)境訪問(wèn)內(nèi)部環(huán)境的變量。閉包使得函數(shù)可以繼續(xù)訪問(wèn)定義時(shí)的詞法作用域。 閉包是真的讓人頭暈啊,看了很久還是覺(jué)得很模糊。只能把目前自己的一些理解先寫(xiě)下來(lái),這其中必定包含著一些錯(cuò)誤,待日后有更深刻的理解時(shí)再作更改。 吐槽一下,閉包這個(gè)詞的翻譯真是有很大的誤解性啊…… 要說(shuō)閉包,要先說(shuō)下詞法...
摘要:如果提升改變了代碼執(zhí)行的順序,會(huì)造成非常嚴(yán)重的破壞。聲明本身會(huì)被提升,而包括函數(shù)表達(dá)式的賦值在內(nèi)的賦值操作并不會(huì)提升。要注意避免重復(fù)聲明,特別是當(dāng)普通的聲明和函數(shù)聲明混合在一起的時(shí)候,否則會(huì)引起很多危險(xiǎn)的問(wèn)題 你不知道的JS(上卷)筆記 你不知道的 JavaScript JavaScript 既是一門(mén)充滿吸引力、簡(jiǎn)單易用的語(yǔ)言,又是一門(mén)具有許多復(fù)雜微妙技術(shù)的語(yǔ)言,即使是經(jīng)驗(yàn)豐富的 Ja...
閱讀 3421·2021-10-20 13:49
閱讀 2793·2021-09-29 09:34
閱讀 3691·2021-09-01 11:29
閱讀 3081·2019-08-30 11:01
閱讀 838·2019-08-29 17:10
閱讀 866·2019-08-29 12:48
閱讀 2776·2019-08-29 12:40
閱讀 1346·2019-08-29 12:30