iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 3
1

今天來聊聊變數的作用範圍。

C 語系

在其他程式語言像是 C 語系中,變數的活動範圍(scope)為區塊(block),也就是在每個 { } 中,都屬於一個新的活動範圍。

例如下面的範例中,依序輸出 1、0,而不會提示說重複宣告。

int main() {
  int myVar = 0;
  {
    int myVar = 1;
    printf("inner scope: %d\n", myVar); // 1
  }
  printf("main scope: %d\n", myVar); // 0
  return 0;
}

JS

Before ES6

在 ES6 之前,只有兩種 scope

  1. 全域活動範圍(global scope)
  2. 函數活動範圍(function scope)

也就是活動範圍不是 block,而是 function。
帶入剛剛的例子:

var myVar = 0;
  {
    var myVar = 1;
    console.log("inner scope: %d\n", myVar); // 1
  }
console.log("main scope: %d\n", myVar); // 1

注意到了嗎?
在 block({ }) 中的變數直接修改到了外面的變數,前後印出都是 1

每定義一個函數,就會建立一個屬於這個函數的活動範圍,不在函數內的變數就屬於全域活動範圍。

ES6 以前,只能使用 var 來宣告變數,在函數中以 var 宣告的變數,其活動範圍僅在函數內,外部無法使用,而沒有用 var 或是在函數外宣告的變數,就屬於全域範圍。

var 是看函數,而不是區塊

Since ES6

但是從 ES6 開始,多了兩種宣告變數的方式: letconst

let

let 不同於 var,它是以區塊為活動範圍,在 ES6 之後,你可以在 for 區塊、if 區塊、或是純區塊中,使用 let 宣告以區塊為活動範圍的變數。

let myVar = 0;
  {
    let myVar = 1;
    console.log("inner scope: %d\n", myVar); // 1
  }
console.log("main scope: %d\n", myVar); // 0

let vs var

  • let 禁止在同一活動範圍中再次宣告相同名稱的變數。
  • var 會無視第二次宣告,直接指派變數值。
  • let 禁止在宣告變數之前就使用它。
  • 在全域範圍以 let 宣告的變數,不會成為全域個體(global object)的屬性。但以 var 宣告的變數同時也會是全域個體的屬性,因此 let 變數是真正的區域變數。

let 是看區塊,而不是函數。

const

使用 const 宣告的變數,顧名思義就是無法再更動其值,也是常數的概念。若試圖改變 const 宣告之變數,都是語法錯誤。

除此之外,const 的語法限制和 let 相同:

  1. 不允許重複宣告。
  2. 不允許宣告前使用。

const 是看區塊,而不是函數

總結

最後用這個例子統整一下上方講的,可以看到在 function varScope 的 for 區塊中,可以使用區塊外 varlet 宣告之變數;在區塊外可以使用區塊內 var 宣告之變數,但卻無法使用 let 宣告之變數。

function varScope(){
  var outside_var = "outside_var";
  let outside_let = "outside_let";

  for(let i = 0; i < 1; i++){
    var inner_var = "inner_var";
    let inner_let = "inner_let";

    console.log(outside_var); // outside_var
    console.log(outside_let); // outside_let
  }
  
  console.log(inner_var); // inner_var
  console.log(inner_let); // error: inner_let is not defined
}

varScope();

那今天的分享就到這邊,明日會聊聊 hoisting/images/emoticon/emoticon69.gif

等等,再補充一個東西好了,如果有一個物件長這樣

const myObj = {
  url: "http://123.com"
}
myObj.url = "changed";
console.log(myObj.url); // changed

咦! const 宣告的變數不是不能夠修改了嗎?

之所以不會有錯誤,是因為在 JS 中陣列物件都是屬於 reference type,在修改時並沒有把這個常數指向其他地方。預計之後 傳值(value)傳址(reference) 單元會再解釋。

但如果真的不想被修改,可以用這個方法

Object.freeze(myObj);
myObj.url = "changed";
console.log(myObj.url); // "http://123.com"

好啦,明天見!/images/emoticon/emoticon51.gif


上一篇
Day2 變數型別
下一篇
Day4 Hoisting & Scope Chain
系列文
從0.5開始的JavaScript30

1 則留言

0
konekoya
iT邦新手 5 級 ‧ 2018-10-08 09:17:18

寫的不錯喔!加油!

我要留言

立即登入留言