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