摘要:詞法作用域定義在詞法階段的作用域由你在寫代碼時將變量和塊作用域寫在哪來決定的,因此當詞法分析器處理代碼時會保持作用域不變。欺騙詞法作用域在詞法分析器處理過后依然可以修改作用域。
你不知道的JS(上卷)筆記
你不知道的 JavaScript
JavaScript 既是一門充滿吸引力、簡單易用的語言,又是一門具有許多復雜微妙技術的語言,即使是經驗豐富的 JavaScript 開發者,如果沒有認真學習的話也無法真正理解它們.
上卷包括倆節:
作用域和閉包
this 和對象原型
作用域和閉包希望 Kyle 對 JavaScript 工作原理每一個細節的批判性思 考會滲透到你的思考過程和日常工作中。知其然,也要知其所以然。
詞法作用域作用域共有倆種主要的工作模型: 詞法作用域和動態作用域。
詞法階段詞法化:大部分標準語言編譯器的第一個工作階段叫作詞法化(也叫單詞化)。
詞法化的過程會對源代碼中的字符進行檢查,如果是有狀態的解析過程,還會賦 予單詞語義。
詞法作用域
定義在詞法階段的作用域
由你在寫代碼時將變量和塊作用域寫在哪來決定的,因此當詞法分析器處理代碼時會保持作用域不變。
欺騙詞法作用域: 在詞法分析器處理過后依然可以修改作用域。
事實上,讓詞法作用域根據詞法關系保持書寫時的自然關系不變是一個非常好的最佳實踐。
“作用域氣泡法” 劃分作用域
查找作用域氣泡的結構和互相之間的位置關系給引擎提供了足夠的位置信息,引擎用這些信息來查找標識符的位置。
作用域查找始終從運行時所處的最內部作用域開始,逐級向外或者向上進行。
作用域查找會在找到第一個匹配的標識符時停止,或者直至找到最后一個全局作用域處。
window.a的方式可以訪問那些被同名變量遮蔽了的全局變量,但非全局變量如果被遮蔽,就無法訪問到了
遮蔽效應: 在多層的嵌套作用域中可以定義同名的標識符。
欺騙詞法倆種欺騙手段:eval和with;
社區認為使用這倆種機制并不是什么好主意,因為使用這倆種機制會導致性能下降
另外一個不推薦使用 eval(..) 和 with 的原因是會被嚴格模式所影響(限 制)。with 被完全禁止,而在保留核心功能的前提下,間接或非安全地使用 eval(..) 也被禁止了。
JavaScript 中的 eval(..) 函數可以接受一個字符串為參數,并將其中的內容視為好像在書 寫時就存在于程序中這個位置的代碼。換句話說,可以在你寫的代碼中用程序生成代碼并 運行,就好像代碼是寫在那個位置的一樣。
function foo(str, a) { eval( str ); // 欺騙! console.log( a, b ); } var b = 2; foo( "var b = 3;", 1 ); // 1, 3
eval通常被用來執行動態創建的代碼
在這個例子中,為了展示的方便和簡潔,我們傳遞進去的“代碼”字符串是 固定不變的。而在實際情況中,可以非常容易地根據程序邏輯動態地將字符 拼接在一起之后再傳遞進去。eval(..) 通常被用來執行動態創建的代碼,因 為像例子中這樣動態地執行一段固定字符所組成的代碼,并沒有比直接將代 碼寫在那里更有好處。
在嚴格模式的程序中,eval(..) 在運行時有其自己的詞法作用域,意味著其 中的聲明無法修改所在的作用域。
類似:setTimeout的第一個參數為字符串時;new Function的最后一個字符串參數;等都不提倡,不要使用。
withwith 通常被當作重復引用同一個對象中的多個屬性的快捷方式,可以不需要重復引用對象 本身。
例如:
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; } // 但實際上這不僅僅是為了方便地訪問對象屬性。考慮如下代碼: function foo(obj) { with (obj) { 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——不好,a 被泄漏到全局作用域上了!
可以注意到一個奇怪的副作用,實際上 a = 2 賦值操作創建了一個全局的變量 a。這 是怎么回事?
with 可以將一個沒有或有多個屬性的對象處理為一個完全隔離的詞法作用域,因此這個對 象的屬性也會被處理為定義在這個作用域中的詞法標識符。
盡管 with 塊可以將一個對象處理為詞法作用域,但是這個塊內部正常的 var 聲明并不會被限制在這個塊的作用域中,而是被添加到 with 所處的函數作 用域中。
with 這種將對象及其屬性放進一個作用域并同時分配標識符的行為很讓人費解。
性能JavaScript 引擎會在編譯階段進行數項的性能優化。其中有些優化依賴于能夠根據代碼的 詞法進行靜態分析,并預先確定所有變量和函數的定義位置,才能在執行過程中快速找到 標識符。
小結詞法作用域意味著作用域是由書寫代碼時函數聲明的位置來決定的。編譯的詞法分析階段 基本能夠知道全部標識符在哪里以及是如何聲明的,從而能夠預測在執行過程中如何對它 們進行查找。
JavaScript 中有兩個機制可以“欺騙”詞法作用域:eval(..) 和 with。前者可以對一段包 含一個或多個聲明的“代碼”字符串進行演算,并借此來修改已經存在的詞法作用域(在 運行時)。后者本質上是通過將一個對象的引用當作作用域來處理,將對象的屬性當作作 用域中的標識符來處理,從而創建了一個新的詞法作用域(同樣是在運行時)。
這兩個機制的副作用是引擎無法在編譯時對作用域查找進行優化,因為引擎只能謹慎地認 為這樣的優化是無效的。使用這其中任何一個機制都將導致代碼運行變慢。不要使用它們。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/108971.html
摘要:如果提升改變了代碼執行的順序,會造成非常嚴重的破壞。聲明本身會被提升,而包括函數表達式的賦值在內的賦值操作并不會提升。要注意避免重復聲明,特別是當普通的聲明和函數聲明混合在一起的時候,否則會引起很多危險的問題 你不知道的JS(上卷)筆記 你不知道的 JavaScript JavaScript 既是一門充滿吸引力、簡單易用的語言,又是一門具有許多復雜微妙技術的語言,即使是經驗豐富的 Ja...
摘要:詞法作用域的查找規則是閉包的一部分。因此的確同閉包息息相關,即使本身并不會真的使用閉包。而上面的創建一個閉包,本質上這是將一個塊轉換成一個可以被關閉的作用域。結合塊級作用域與閉包模塊這個模式在中被稱為模塊。 你不知道的JS(上卷)筆記 你不知道的 JavaScript JavaScript 既是一門充滿吸引力、簡單易用的語言,又是一門具有許多復雜微妙技術的語言,即使是經驗豐富的 Jav...
摘要:如果是聲明中的第一個詞,那么就是一個函數聲明,否則就是一個函數表達式。給函數表達式指定一個函數名可以有效的解決以上問題。始終給函數表達式命名是一個最佳實踐。也有開發者干脆關閉了靜態檢查工具對重復變量名的檢查。 你不知道的JS(上卷)筆記 你不知道的 JavaScript JavaScript 既是一門充滿吸引力、簡單易用的語言,又是一門具有許多復雜微妙技術的語言,即使是經驗豐富的 Ja...
摘要:的抽象語法樹中可能會有一個叫作的頂級節點,接下來是一個叫作它的值是的子節點,以及一個叫作的子節點。值得注意的是,是非常重要的異常類型。嚴格模式下,未聲明的和倆者行為相同,都會是。 你不知道的JS(上卷)筆記 你不知道的 JavaScript JavaScript 既是一門充滿吸引力、簡單易用的語言,又是一門具有許多復雜微妙技術的語言,即使是經驗豐富的 JavaScript 開發者,如果...
iKcamp官網:http://www.ikcamp.com 訪問官網更快閱讀全部免費分享課程:《iKcamp出品|全網最新|微信小程序|基于最新版1.0開發者工具之初中級培訓教程分享》。包含:文章、視頻、源代碼 showImg(https://segmentfault.com/img/remote/1460000011522427?w=1626&h=1242); 第二章:小程序中級實戰教程之預...
閱讀 797·2019-08-30 15:55
閱讀 1543·2019-08-30 15:52
閱讀 2704·2019-08-30 15:44
閱讀 2125·2019-08-30 11:14
閱讀 2634·2019-08-29 13:59
閱讀 1836·2019-08-29 13:45
閱讀 1023·2019-08-29 13:21
閱讀 3385·2019-08-26 13:31