範疇的運作方式主要有兩種模型,
第一種就是今天的主題語彙範疇
,
是大多數的程式語言所用的,
第二種叫做動態範疇
,
它則是少數語言(Bash, Perl 的某些模式)使用的。
接著我們就來好好的跟他交朋友吧
昨天那篇有提到傳統編譯式語言通常的三步驟的第一步就是 lexing
也就是語彙分析
,
而語彙範疇就是在這個時期所定義的範疇(聽起來有點繞口),而這範疇其實就是你在寫 code 時所編寫的"變數"和"區塊",
我自己在高中的時候,計概老師都叫他 Block ,那年夏天老師溫柔的握著我的手把 Block 畫出來的那種感覺。
(做夢中)
如圖所示
Bubble 1 最外圈就是全域範疇
Bubble 2 包含 foo 的範疇
Bubble 3 包含 boo 的範疇
Bubble 1 包含 Bubble 2 + Bubble 3
Bubble 2 包含 Bubble 3
這樣應該就知道了吧!
由上面的範疇泡泡可以知道結構與相對位置,而最內層的 console.log
,就會一路從最內層開始找,找到最外層直到只要找到"第一個符合",就會停止查找,
不管該函式在哪邊"被"用,語彙範疇只會管你在哪被宣告的。
JS 有兩個機制是會影響到語彙範疇的,而且在社群上已經被唾棄,幾乎是完全不建議使用。
那我們就先從 with
來看看吧
我自己之前沒看過和用過,並且已經棄用了,他主要是讓我們可以對一個物件進行多次參考,而不用每次都一直參考該物件本身。
範例
正常情況下
obj.name = 'robin';
obj.age = '18';
obj.phone = '0912345678';
使用 with
with(obj){
name = 'robin';
age = '18';
phone = '0912345678';
}
看起來不錯啊!?
真放便而且清楚明瞭。
但是他被唾棄且棄用...
why!?
因為...
他的霸氣會外漏
來看看下面的範例
function foo(){
with(obj){
a = 3;
}
}
var obj1 = {
a = 4;
}
var obj2 = {
b = 4;
}
foo(obj1);
console.log(obj1.a); // 3
foo(obj2);
console.log(obj2.a); // undefined
console.log(a); // 3 shit 變成全域變數了
看到這你一定會想說...
原因是這樣~
第一個 obj1
本身就有 a
這個特性,
而第二個 obj2
沒有 a
這個特性...
然後...
它就直接略過 with
對 a
做正常語彙參考一樣直接往外找,
找不到 a
直接幫他建了一個 a
並且塞 3
這個值給他。
不過這樣講起來,經過昨天的 LHS
和 RHS
的講解後,其實你就已經有一個底直接開啟嚴格模式,其實就不會發生這類型的 side effect 了。
我以前用過 Orz... 而且被糾正過。
但是其實我當初也不太知道為何不能用,那我們就一起來看看他有些什麼問題吧。
eval
的主要用途是在於他可以把一段 String 轉為程式碼,並且可以正常執行。
eval('console.log('hi')') // hi
那他為何被唾棄?
因為他在一開始的時候 Engine
不會知道他的內容造成他欺騙語彙範疇,就有點類似半路殺出程咬金的感覺xD
來看看範例吧
function foo(str, a){
eval(str);
console.log(a,b); // 8,6
}
var b = 7;
foo('var b = 6',7);
原本你以為的結果會是 8,7
,結果突然因為 eval 導致結果變 8,6
,與你預期的不符合。
不管事 eval
還是 with
,他們都是在執行的時候才修改或建立新的語彙範疇,
之前提過 JS 在編譯階段會做各種最佳化,那當他們遇到上述兩個的時候,他們會直接略過不管,不會去解析裡面的內容,導致最佳化的時候沒有被最佳化所以效能會變比較差...
以上是今天的內容
感謝您的收看
如果喜歡請按讚訂閱加分享
明天見~
你所不知道的 JS|範疇與 Closures,this 與物件原型 (You Don't Know JS: this & Object Prototypes))