摘要:查詢是在作用域鏈中,一級級的往上查找該變量的引用。作用域和作用域鏈作用域的概念,應該兩張圖幾句話就能解釋吧。這個建筑代表程序中的嵌套作用域鏈。一層嵌一層的作用域形成了作用域鏈,變量在作用域鏈中的函數(shù)內得到了自己的定義。
javascript作用域和閉包之我見
看了《你不知道的JavaScript(上卷)》的第一部分——作用域和閉包,感受頗深,遂寫一篇讀書筆記加深印象。路過的大牛歡迎指點,對這方面不懂的同學請繞道看書,以免誤人子弟... 看過這本書的可以一起交流交流。
編譯過程理解js作用域首先要了解js的編譯過程(或者說解析過程)。
引擎
從頭到尾負責整個 JavaScript 程序的編譯及執(zhí)行過程。
編譯器
引擎的好朋友之一,負責語法分析及代碼生成等臟活累活(詳見前一節(jié)的內容)。
作用域
引擎的另一位好朋友,負責收集并維護由所有聲明的標識符(變量)組成的一系列查詢,并實施一套非常嚴格的規(guī)則,確定當前執(zhí)行的代碼對這些標識符的訪問權限。
都說node是基于chrome的V8引擎開發(fā) 的。那么V8是引擎,node是編譯器嗎?這個理解是錯誤的!我之前就是這么錯誤理解的,聽說node是用C++實現(xiàn)的,之前我一直以為V8是負責把javascript語言轉換成底層的C++,然后node很高級node負責編譯,做js的語法檢察,ES6的新特性全都是node的開發(fā)人員,一點點的開發(fā)支持起來的。然而現(xiàn)實是,V8包辦了所有js編譯的過程,而node只是一個環(huán)境。如nodejs.cn首頁所說Node.js 是一個基于 Chrome V8 引擎的 JavaScript 運行環(huán)境。 ,是運行環(huán)境!node只是在V8的基礎上,做了終端命令行的支持、文件處理的支持、http服務的支持等等,相當于一個給V8提供了各種功能的殼子。
上面說的三點是包含關系,不是并行關系!引擎包含編譯器,對js進行編譯,然后根據(jù)作用域和語句執(zhí)行不同的代碼邏輯。
編譯器的查詢我們將 var a = 2; 分解,看看引擎和它的朋友們是如何協(xié)同工作的。
編譯器首先會將這段程序分解成詞法單元,然后將詞法單元解析成一個樹結構。但是當編 譯器開始進行代碼生成時,它對這段程序的處理方式會和預期的有所不同。
可以合理地假設編譯器所產(chǎn)生的代碼能夠用下面的偽代碼進行概括:“為一個變量分配內 存,將其命名為 a,然后將值 2 保存進這個變量。”然而,這并不完全正確。
事實上編譯器會進行如下處理。
1. 遇到var a,編譯器會詢問作用域是否已經(jīng)有一個該名稱的變量存在于同一個作用域的 集合中。如果是,編譯器會忽略該聲明,繼續(xù)進行編譯;否則它會要求作用域在當前作 用域的集合中聲明一個新的變量,并命名為 a。 2. 接下來編譯器會為引擎生成運行時所需的代碼,這些代碼被用來處理a = 2這個賦值 操作。引擎運行時會首先詢問作用域,在當前的作用域集合中是否存在一個叫作 a 的 變量。如果是,引擎就會使用這個變量;如果否,引擎會繼續(xù)查找該變量。
如果引擎最終找到了 a 變量,就會將 2 賦值給它。否則引擎就會舉手示意并拋出一個異常!
在我們的例子中,引擎會為變量 a 進行 LHS 查詢。另外一個查找的類型叫作 RHS。
RHS 查詢與簡單地查找某個變量的值別無二致,而 LHS 查詢則是試圖 找到變量的容器本身,從而可以對其賦值。從這個角度說,RHS 并不是真正意義上的“賦 值操作的右側”,更準確地說是“非左側”。
你可以將RHS理解成retrieve his source value(取到它的源值),這意味著“得到某某的 值”。
怎么理解呢,我的理解是LHS 查詢是查詢變量的命名空間,然后進行賦值。RHS 查詢是在作用域鏈中,一級級的往上查找該變量的引用。
所以:
function foo(a) { var b=a; return a + b; } var c=foo(2);找到其中所有的LHS查詢。(這里有3處!)
找到其中所有的RHS查詢。(這里有4處!)
LHS:var c=的賦值、foo(2)傳參給foo(a)時的賦值、var b=的賦值
RHS:foo(2)函數(shù)調用時查找foo()方法、var b=a中a查找自己的值、a+b中a和b兩個參數(shù)查找自己的值。
作用域的概念,應該兩張圖幾句話就能解釋吧。
這個建筑代表程序中的嵌套作用域鏈。第一層樓代表當前的執(zhí)行作用域,也就是你所處的 位置。建筑的頂層代表全局作用域。
LHS 和 RHS 引用都會在當前樓層進行查找,如果沒有找到,就會坐電梯前往上一層樓, 如果還是沒有找到就繼續(xù)向上,以此類推。一旦抵達頂層(全局作用域),可能找到了你 所需的變量,也可能沒找到,但無論如何查找過程都將停止。
① 包含著整個全局作用域,其中只有一個標識符:foo。
② 包含著 foo 所創(chuàng)建的作用域,其中有三個標識符:a、bar 和 b。
③ 包含著 bar 所創(chuàng)建的作用域,其中只有一個標識符:c。
作用域氣泡由其對應的作用域塊代碼寫在哪里決定,它們是逐級包含的。
我覺得,說一個變量屬于哪個作用域,可以顧名思義用該變量生效的區(qū)域來解釋,所以上圖中的b變量,可以說屬于bar()的函數(shù)作用域內,也可以說是foo()的函數(shù)作用域內,也可以說是全局作用域內。
一層嵌一層的作用域形成了作用域鏈,變量b在作用域鏈中的foo()函數(shù)內得到了自己的定義。
eval(..) 和 with 會在運行時修改或創(chuàng)建新的作用域,以此來欺騙其他在書寫時定義的詞法作用域。
JavaScript 引擎會在編譯階段進行數(shù)項的性能優(yōu)化。其中有些優(yōu)化依賴于能夠根據(jù)代碼的 詞法進行靜態(tài)分析,并預先確定所有變量和函數(shù)的定義位置,才能在執(zhí)行過程中快速找到 標識符。
但如果引擎在代碼中發(fā)現(xiàn)了 eval(..) 或 with,它只能簡單地假設關于標識符位置的判斷 都是無效的,因為無法在詞法分析階段明確知道 eval(..) 會接收到什么代碼,這些代碼會 如何對作用域進行修改,也無法知道傳遞給 with 用來創(chuàng)建新詞法作用域的對象的內容到底 是什么。這兩個機制的副作用是引擎無法在編譯時對作用域查找進行優(yōu)化,因為引擎只能謹慎地認 為這樣的優(yōu)化是無效的。使用這其中任何一個機制都將導致代碼運行變慢。不要使用它們。
call()、bind()之類的是改變作用域嗎?他們只是改變了this的指向并不算改變作用域,是可以在編譯階段進行靜態(tài)分析,所以不會導致上面說的無法優(yōu)化的情況。
形成作用域我們知道函數(shù)可以形成作用域,還有哪些方式形成作用域呢?
with可以指定變量的作用域(選擇一個對象),在它的塊作用域內,變量就相當于這個對象的屬性。
var obj={ a: 1, b: 2, c:3 }; // 單調乏味的重復 "obj" obj.a = 2; obj.b = 3; obj.c = 4; // 簡單的快捷方式 with (obj) { a=3; b=4; c=5; }
不被推薦,因為它會影響性能,且不易閱讀(代碼塊內的代碼特別多的情況,根本不知道這個是普通的變量還是某個對象的屬性,還是某個對象的屬性的屬性的屬性)。
try/catchtry { undefined(); // 執(zhí)行一個非法操作來強制制造一個異常 } catch (err) { console.log( err ); // 能夠正常執(zhí)行! } console.log( err ); // ReferenceError: err not found
做錯誤狀態(tài)傳參的err變量是當前塊的局部變量。
但是如果在catch(err){…}內部var其它變量,并沒有效果,見下面代碼。
try { var abc="測試try塊中的變量" } catch (err) { var b=2; // 沒有錯誤,不會被執(zhí)行到的。 } console.log( abc ); // 測試try塊中的變量
try { throw "55"; // 制造一個異常 } catch (err) { var abc="測試catch塊中的變量"; } console.log(abc); // 測試catch塊中的變量
這是只屬于err參數(shù)用的偽塊作用域。
let、constES6新特性,大神器。在{}中形成塊作用域,且不會遇到提升 的問題出現(xiàn)。
為變量顯式聲明塊作用域,有助于回收內存垃圾。
function process(data) { // 在這里做點有趣的事情 } // 在這個塊中定義的內容可以銷毀了! (這里指的是下面let定義的`someReallyBigData`) { let someReallyBigData = { .. }; process( someReallyBigData ); } var btn = document.getElementById( "my_button" ); btn.addEventListener( "click", function click(evt){ console.log("button clicked"); }, /*capturingPhase=*/false );
let有一個很有意思的地方,就是在for循環(huán)中。
提升for (let i=0; i<10; i++) { console.log( i ); } console.log( i ); // ReferenceErrorfor 循環(huán)頭部的 let 不僅將 i 綁定到了 for 循環(huán)的塊中,事實上它將其重新綁定到了循環(huán) 的每一個迭代中,確保使用上一個循環(huán)迭代結束時的值重新進行賦值。
下面通過另一種方式來說明每次迭代時進行重新綁定的行為:{ let j; for (j=0; j<10; j++) { let i = j; // 每個迭代重新綁定! console.log( i ); } }
編譯器在解析作用域時,會對作用域中var聲明的變量、函數(shù)進行提升。
a=2; var a; console.log( a ); // 2
相當于
var a; a=2; console.log( a ); // 2
console.log( a ); // undefined var a=2;
相當于
var a; console.log( a ); // undefined a=2;
函數(shù)聲明和變量聲明都會被提升。但是一個值得注意的細節(jié)(這個細節(jié)可以出現(xiàn)在有多個“重復”聲明的代碼中)是函數(shù)會首先被提升,然后才是變量。
foo(); // 1 var foo; function foo() { console.log( 1 ); } foo = function() { console.log( 2 ); };
相當于
function foo() { console.log( 1 ); } foo(); // 1 foo = function() { console.log( 2 ); };閉包
當函數(shù)可以記住并訪問所在的詞法作用域,即使函數(shù)是在當前詞法作用域之外執(zhí)行,這時就產(chǎn)生了閉包。
function foo() { var a=2; function baz() { console.log( a ); // 2 } bar( baz ); } function bar(fn) { fn(); // 媽媽快看呀,這就是閉包! }
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/81973.html
摘要:是結構在框架中的一種表現(xiàn)形式。這句話聽起來有點繞,我們可以看一下下面這張圖,大家可以吧容器中的理解為全局變量,吧容器中的理解為局部變量。在方法體中可以訪問局部變量和全局變量,但是在方法外,就只能訪問全局變量,是不能訪問局部變量的。 聊完了Spring,我們來看看Spring在web方面的應用Spring MVC。 MVC 首先我們來看什么是mvc? 大家知道在jsp里面也是可以寫jav...
摘要:即使秒殺系統(tǒng)崩潰了,也不會對網(wǎng)站造成影響。動態(tài)生成隨機下單頁面的為了避免用戶直接訪問下單需要將動態(tài)化,用隨機數(shù)作為參數(shù),只能秒殺開始的時候才生成。架構設計如何控制秒殺商品頁面搶購按鈕的可用禁用。該文件不被緩存的做法隨機數(shù)。 秒殺背景 電商中為了吸引顧客、聚集人氣,經(jīng)常會策劃一些秒殺活動。活動中售賣的商品,要么價格遠低于市場價格,要么比較稀缺(如一些新發(fā)布的商品)。這些商品電商一般都會限...
摘要:即使秒殺系統(tǒng)崩潰了,也不會對網(wǎng)站造成影響。動態(tài)生成隨機下單頁面的為了避免用戶直接訪問下單需要將動態(tài)化,用隨機數(shù)作為參數(shù),只能秒殺開始的時候才生成。架構設計如何控制秒殺商品頁面搶購按鈕的可用禁用。該文件不被緩存的做法隨機數(shù)。 秒殺背景 電商中為了吸引顧客、聚集人氣,經(jīng)常會策劃一些秒殺活動。活動中售賣的商品,要么價格遠低于市場價格,要么比較稀缺(如一些新發(fā)布的商品)。這些商品電商一般都會限...
摘要:即使秒殺系統(tǒng)崩潰了,也不會對網(wǎng)站造成影響。動態(tài)生成隨機下單頁面的為了避免用戶直接訪問下單需要將動態(tài)化,用隨機數(shù)作為參數(shù),只能秒殺開始的時候才生成。架構設計如何控制秒殺商品頁面搶購按鈕的可用禁用。該文件不被緩存的做法隨機數(shù)。 秒殺背景 電商中為了吸引顧客、聚集人氣,經(jīng)常會策劃一些秒殺活動。活動中售賣的商品,要么價格遠低于市場價格,要么比較稀缺(如一些新發(fā)布的商品)。這些商品電商一般都會限...
閱讀 2940·2023-04-26 01:52
閱讀 3468·2021-09-04 16:40
閱讀 3629·2021-08-31 09:41
閱讀 1764·2021-08-09 13:41
閱讀 555·2019-08-30 15:54
閱讀 2959·2019-08-30 11:22
閱讀 1612·2019-08-30 10:52
閱讀 947·2019-08-29 13:24