javascript
你不知道的 JavaScript 笔记——作用域和闭包
第一章:作用域是什么
程序中變量存儲在哪里,需要是怎么找到它,這就需要設(shè)計一套存儲以及能方便的找到它的規(guī)則,這個規(guī)則就是作用域
編譯原理
JavaScript 是一門編譯語言,它與傳統(tǒng)編譯語言不同,但編譯步驟又非常相似;它不是提前編譯的,編譯結(jié)果也不能在分布式系統(tǒng)中進行移植。
傳統(tǒng)編譯步驟
分詞 / 詞法分析
將由字符組成的字符串分解成有意義的代碼,例如:var a = 2;通常會被分解為var、a、=、;這些詞法單元,判斷a是獨立的詞法單元還是其他詞法單元一部分時,如果調(diào)用的是有狀態(tài)的解析規(guī)則那這個過程就是詞法分析。
解析 / 語法分析
將詞法單元流(數(shù)組)轉(zhuǎn)換成一個由元素逐級嵌套所組成的代表程序語法結(jié)構(gòu)的樹,叫做“抽象語法樹”(Abstract Syntax Tree,AST)
代碼生成
將 AST 轉(zhuǎn)換為可執(zhí)行代碼的過程被稱為代碼生成。簡單說就是:將var a = 2;的 AST 轉(zhuǎn)化為一組機器指令,用來創(chuàng)建一個a的變量(包括分配內(nèi)存),并將一個值存儲在a中。
理解作用域
var a = 2;在 JavaScript 引擎看來是兩個完全不同的聲明。
LHS查詢 RHS查詢
LHS 賦值操作的目標是誰;RHS 誰是賦值操作的源頭(我要查找×××,并把它給我);簡單說如果查找的目的是對變量進行賦值,會使用 LHS 查詢,如果查找的目的是為了獲取變量值,會使用 RHS 查詢。
var a = foo(a){...} 這里的聲明,a = ...并不會做 LHS 查詢。
第二章:詞法作用域
詞法作用域是在寫代碼時,將變量和塊級作用域?qū)懺谀睦飦頉Q定的
詞法階段
function foo(a){var b = a * 2;function bar(c){console.log(a,b,c); //2,4,12}bar( b * 3); } foo(2);對b、c的 RHS 查詢是在上一級作用域中完成的
欺騙詞法
eval()接受一個字符串作為參數(shù),把它當(dāng)做代碼來執(zhí)行,在哪個作用域下調(diào)用它,它的作用域就在哪邊
function foo(str,a){eval(str);console.log(a,b); //2,3 } var b = 2; foo('var b = 3;',2)with可以簡化單調(diào)重復(fù)的賦值操作
如
eval()、with實際工作中不推薦使用,會影響性能。
第三章:函數(shù)作用域和塊作用域
函數(shù)中的作用域
函數(shù)內(nèi)部可以訪問函數(shù)外部的變量,函數(shù)外部不可以訪問函數(shù)內(nèi)部變量
函數(shù)作用域
函數(shù)的名稱也會污染全局作用域,可以用匿名函數(shù)+立即執(zhí)行函數(shù)來實現(xiàn)
立即執(zhí)行函數(shù)
有一個bug,上一行表達式必須要分號結(jié)尾,省略會報錯
var a = 2; (function(global){var a = 3;console.log(a); //3console.log(global.a) //2 })(window) var a = 2; (function(){var a = 3;console.log(a); //3 }())作用域閉包
1. 在自己定義的作用域以外的地方執(zhí)行
2. 使用回調(diào)函數(shù)也是閉包
bar是在foo里面定義的,所以它是在foo作用域下,但調(diào)用它的地方是在foo作用域外,就這構(gòu)成了閉包。
在來看一個
還有
var fn function foo(){var a =2function bar(){console.log(a)}fn = baz } function baz(){fn() } foo() baz() //2,構(gòu)成了閉包,這里執(zhí)行了 bar,構(gòu)成了閉包。在循環(huán)中使用閉包
閉包是函數(shù)和聲明該函數(shù)的詞法環(huán)境的組合。
回到我們上面說的:在自己定義的作用域以外的地方執(zhí)行,這里聲明的i是全局變量,使用全局變量不構(gòu)成閉包。
for(var i = 1; i <= 3; i++){setTimeout(function(){console.log(i) //打印出 3 個 4,這里沒有閉包},1000) }如果要寫成閉包的樣子,必須要在外面用函數(shù)包裹一層,并調(diào)用它,才能形成閉包
function xxx(){for(var i = 1; i <= 3; i++){setTimeout(function(){console.log(i) //打印出 3 個 4,這是一個閉包沒有立即執(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){ //這個形參 i 和外面的 i 不是同一個變量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)的過程中不止一次的聲明},1000)} } xxx()模塊中閉包
模塊中閉包需要具備兩個條件:
例:
var foo = function(){function a(){console.log(1)}function b(){console.log(2)}return {a:a,b:b} } var c = foo() c.a() //1總結(jié)
以上是生活随笔為你收集整理的你不知道的 JavaScript 笔记——作用域和闭包的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: TextView显示不同颜色的文本,及文
- 下一篇: S9306开启web功能!