iT邦幫忙

2021 iThome 鐵人賽

DAY 25
0
Modern Web

追求JS小姊姊30天系列 第 25

追求JS小姊姊系列 Day25 -- 工具人、姐妹的存活原理:宣告變數的有效區域

前情提要:

看完記憶體儲存差異,現在要來談談全域污染這件事。

基本scope概念

所謂的範疇Scope是規範變數有效的區域

在JavaScript內有三個主要的變數有效範疇:

  1. global scope
  2. function scope
  3. block scope

透過下面這段程式碼來標示所謂的scope


let x = 2;
let y = 2;
function trys(){
    let x = 3;
    let y = 3;
}

{
    let x = 4;
    let y = 4;
}


Scope 與宣告的關係

不同的宣告有不同的變數有效範疇,以下簡單以表格呈現:

宣告方式 var let const
有效區域 global scope / function scope global scope / funciotn scope / block scope global scope / funciotn scope / block scope

有效區域指的是什麼意思,也直接看例子吧?


//區域範疇內有x變數
{
    let x = 3;
    
}
console.log(x);

可以理解成x這個宣告,出了這個區域就會無效(無法取得)。

其實之所以會出現所謂scope的現象,背後是由字彙環境一手包辦的,什麼意思?


從字彙環境的視角詮釋

字彙環境(lexical environment)的任務就是:追蹤紀錄範疇內(scope)的識別字

let x = 2;
let y = 2;
function trys(){
    let x = 3;
    let y = 3;
}

{
    let x = 4;
    let y = 4;
}

會透過字彙環境紀錄自己區域內的宣告內容,在你需要使用時就會來找尋

當你要找尋的變數,不存在當前的字彙環境中,它會往上層的字彙環境找尋,若到了全域都沒有此變數,則會回報reference error

假設我們今天在函式內會取用到z這個變數,但它不存在:

let z = "我在全域";

function doIt(i){
    console.log(z);
}

doIt();


最後由圖可知,變數z會在全域找到。

理念大概理解,用實例來試試看吧


重新理解for迴圈結構!

來看一個經典的範疇(scope)相關題目

當我們用var宣告時

for(var i =0;i <4;i++){
    setTimeout(function(){
        console.log(i,"我是結果");
    },1000)
}

你想像的執行過程跟的結果

i = 0; console.log(0,"我是結果");
i = 1; console.log(1,"我是結果");
i = 2; console.log(2,"我是結果");   
i = 3; console.log(3,"我是結果");
i = 4; console.log(4,"我是結果");
//0,"我是結果";
//1,"我是結果";
//2,"我是結果";
//3,"我是結果";
//4,"我是結果";

實際上的結果

因為

  1. 非同步的關係,它實際上是跑完迴圈後
  2. 字彙環境的原因

varlet的差別

前面有提過兩者的範疇上的差異:

var時,因為它不具有block scope,所以並不存在{}的字彙環境,因此不會紀錄i的值,所以會往global environment尋找此值,

i = 0;
i = 1;
i = 2;
i = 3;
i = 4;

console.log(4);
console.log(4);
console.log(4);
console.log(4);
console.log(4);

而因為非同步的執行,所以當要印出所有i時,global environment所紀錄的值,已經是迭代完成的值,也就是4

那為何let就可以?
前面提過let具有block environment的特性,所以每次迭代時,都會在{}建立字彙環境,並記住當前i的值。

所以你可以想像為:

//開始迭代: for environment
{    
    let i =0;
    console.log(i);
}
//第一圈後: for environment
{
    let i =1;
    console.log(i);
}

...

{
    let i = 4;
    console.log(i);
}

不過上述的論述,只是在學習過程與前輩跟同學討論的整理,並沒有在規範中明確定義。

-- to be continued --


那今天就到這邊摟!
每天的休息,是為了後面的追求,明天見。


reference:

忍者2
所有的函式都是閉包:談 JS 中的作用域與 Closure


上一篇
追求JS小姊姊系列 Day24 -- 工具人、姐妹不只身份的差別(下):從記憶體看宣告變數的可變性
下一篇
追求JS小姊姊系列 Day26 -- 不是被已讀,而是JS回覆你卻沒看到:`console`
系列文
追求JS小姊姊30天30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言