iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 16
0
自我挑戰組

你為什麼不問問神奇 JavaScript 呢?系列 第 16

Day16 - 語彙範疇

語彙分析時期

上一章節 compiler 做的第一個步驟,就是語彙分析 ( lexing )。( 拆單字 )。
語彙分析會做的事:

  1. 檢視原始碼字元
  2. 指定語意 ( semantic meaning ) 給基本單元 ( tokens )。

語彙範疇 ( lexical scope ),就是在語彙分析時定義的範疇。就是寫 code 的人定義的範圍。

function foo(a) {
    var b = a * 2;
    
    function bar(c) {
        console.log( a, b, c );
    }
    bar( b * 3);
}
foo( 2 ); // 2, 4, 12

這個範例程式碼有三個槽狀範疇 ( nested scopes )。

  1. 全域範疇,只有一個識別字:foo。
  2. foo 的範疇,有三個識別字:a、bar 與 b。
  3. bar 的範疇,有一個識別字:c。

範疇泡泡 ( scope bubbles ) 的定義取決於那些範疇區塊 ( blocks of scope )。
這個章節先以函式做範疇的區分。
泡泡一定是包著泡泡,不會有一個泡泡被兩個獨立的泡泡分別包著。

查找 ( Look-ups )

依照前一個章節來拿東西,如果小倉庫沒位置拿,會到大倉庫拿,直到在全球總倉拿到東西。若沒有就報錯。

  1. 拿 a => bar 沒有 => foo 有隱含 LHS。2
  2. 拿 b => bar 沒有 => foo 有 a * 2 = 4
  3. 拿 c => foo 有隱含 LHS,b * 3 = 12

但如果其實大小倉庫都有位置 a,裡面放的東西不一樣。會拿什麼?

小倉庫的,這叫遮蔽 ( shadowing,內層識別字遮蔽了外層識別字 )。

但我還是想拿全球總倉的東西呢?
windows.a

語彙範疇作弊技巧

這兩個技巧被唾棄,能不用就不用,但還是要了解一下。

eval

如果你有條程式碼,想嵌在某個角落。就用 eval。

把這段程式碼寫成字串,就能放到 eval 裡面。

eval("var c = 1;") 等同於 var c = 1;

在全域環境呼叫的字串,就換傳進 function 內部,進而修改內部程式。

function foo(str, a){
    eval( str ); // 作弊
    console.log(a, b);
}
var b = 2;
foo( "var b = 3;", 1 ); // 1, 3

發生什麼事?

  1. 先置換 eval 的程式碼。
  2. foo 範疇的 b 在查找時,以置換後的程式碼當作是查找的結果。
  3. 遮蔽全域宣吿。

但在嚴格模式下,eval 不再起作用。

function foo(str){
    "use strict";
    eval( str );
    console.log( a ); // ReferenceError
}

foo( "var a = 2;" );

其他能接受字串成為程式碼的函式

  1. setTimeout(..) -- 第一個引數
  2. setInterval(..) -- 第一個引數
  3. new Function(..) -- 第二個引數

都是動態的修改程式碼內容。前兩個早被棄用,別這樣用。第三個建構子比較 eval 安全,但還是避免用。

因為用的機會少,還會降低效能喔!

with

已被棄用。之後再來了解棄用的原因。
(感覺是語法糖,但危害到身體健康)

效能

當然會降低。
JavaScript 快速編譯,快速執行。中間會穿插著最佳化。

在 lexing 時期,遇到這兩個傢伙,就會先假設他們識別字是無效的(每個單字),因為你不知道為傳什麼進來。這樣怎麼進行最佳化?

就乾脆不進行了。少了最佳化,就會慢。

參考資料

你所不知道的JS


上一篇
Day15 - 何謂範疇?
下一篇
Day17 - 函式 VS. 區塊範疇
系列文
你為什麼不問問神奇 JavaScript 呢?30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言