跳轉到內容

JavaScript/變數

來自華夏公益教科書,為開放世界提供開放書籍



計算機語言需要使用變數。為什麼呢?在大多數情況下,程式不會解決像“半徑為 5 釐米的圓的周長是多少?”這樣的具體問題。這樣的具體問題無需使用變數即可解決:alert (2 * 5 * 3.14); 相反,大多數問題更加通用:“半徑為任意值的圓的周長是多少?”你不會想要為半徑為 5 釐米寫一個程式,再為半徑為 6 釐米寫一個程式,再為半徑為 7 釐米寫一個程式,如此類推。你想要寫一個能夠計算所有可能半徑的周長的單一程式。該程式需要輸入(來自使用者、來自其他程式、來自資料庫等等)來告知它需要使用哪個值進行執行。let r = prompt("你的圓的半徑是多少?"); alert (2 * r * 3.14);,或者更好的:let r = prompt("你的圓的半徑是多少?"); alert (2 * r * Math.PI);

這兩個例子都具有靈活性。它們向用戶詢問所需的半徑,將給定的值儲存在名為 r 的 **變數** 中,並使用該變數計算周長。變數 r 由關鍵字 let 引入。這裡還有一個變數。模組 Math 用關鍵字 const 預定義了一個名為 PI 的變數:const PI = 3.141592653589793;

在 JavaScript 中,變數的使用方式類似於數學公式中的變數。在執行時,變數的值儲存在計算機的主記憶體(RAM)中,可以稍後在程式的生命週期中使用。你可以將變數想象成一個小盒子,你在裡面放一些值,並在需要時取出來。

變數是將單個問題解決轉變為策略或 **演算法** 的基石。

宣告和初始化

[編輯 | 編輯原始碼]

如果你想使用變數,我們建議你明確地進行 宣告。這不是強制性的,但有很大的好處。

在許多情況下,宣告伴隨著初始化,例如 let x = 0;。宣告是 let x;,初始化是 x = 0;。但也可以省略初始化部分:let x;,這將導致變數的值為 undefined

關鍵字 let

[編輯 | 編輯原始碼]

關鍵字 let 引入一個變數,其值可以多次更改。

let x = 0;
// ...
x = x + 5;
// ...
x = -4;
// ...

關鍵字 const

[編輯 | 編輯原始碼]

關鍵字 const 引入一個變數,該變數 **必須** 立即初始化。此外,這個第一個值永遠不能改變。這有助於 JavaScript 引擎最佳化程式碼,以提高效能。儘可能使用 const

const maxCapacity = 100;
// ...
maxCapacity = maxCapacity + 10; // not possible
let maxCapacity = 110;          // not possible

當你使用物件(例如,陣列)並結合關鍵字 const 時,情況相同:你不能將另一個值(物件、陣列、數字或其他任何值)賦給變數。但是,它的元素可以更改。

const arr = [1, 2, 3];
arr = [1, 2, 3, 4];     // not possible
arr = new Array(10);    // not possible

arr[0] = 5;             // ok:  5, 2, 3
alert(arr);

arr.push(42);           // ok:  5, 2, 3, 42
alert(arr);

在某些情況下,const 變數以大寫形式編寫,例如 PI。這是一種約定,不是強制性的。

關鍵字 var

[編輯 | 編輯原始碼]

乍一看,varlet 相同。但是,這些變數的有效範圍不同於由 var 宣告的變數;請參見下面的 作用域 一章。

省略宣告

[編輯 | 編輯原始碼]

你可以將值賦給變數,而無需事先宣告該變數。“JavaScript 以前允許將值賦給未宣告的變數,這會建立一個 未宣告的全域性變數。這是 嚴格模式 中的錯誤,應該完全避免。”[1] 換句話說:變數進入全域性作用域,請參見下面的 內容。除非你有充分的理由,否則應該避免使用全域性作用域,因為它的使用往往會產生意想不到的副作用。

// direct usage of 'radius' without any keyword for its declaration
/* 1 */ radius = 5;
/* 2 */ alert (2 * radius * 3.14);

有時這種情況是意外發生的。如果原始碼中存在打字錯誤,JavaScript 會使用兩個不同的變數:原始變數和一個使用錯誤型別名稱的新變數——即使你使用 letconstvar 中的任何一個關鍵字。

let radius = 5;  // or without 'let'
alert("Test 1");
// ... later in the code
radus = 1; // typo will not be detected
alert("Test 2");

你可以透過在指令碼的第一行插入命令 "use strict"; 來指示 JavaScript 搜尋此類打字錯誤。

"use strict";
let radius = 5;
alert("Test 1");
// ... later in the code
radus = 1;       // typo will be detected and an error message given
alert("Test 2"); // will never execute

資料型別

[編輯 | 編輯原始碼]

熟悉(嚴格)型別語言(如 Java)的程式設計師可能會在上一章中錯過定義變數型別的可能性。JavaScript 瞭解許多不同的資料型別。但它們的處理和行為與 Java 中的處理和行為非常不同。在 下一章 中,你將瞭解這一點。

作用域

[編輯 | 編輯原始碼]

作用域 是一個連續的 JavaScript 語句範圍,具有明確定義的開始和結束。JavaScript 瞭解四種作用域:塊級作用域、函式作用域、模組作用域和全域性作用域。根據宣告的型別和宣告的位置,變數位於這些作用域中。它們僅在其作用域內是“可見的”或“可訪問的”。如果你嘗試從外部訪問它們,則會發生錯誤。

塊級作用域

[編輯 | 編輯原始碼]

一對花括號 {} 建立一個 。在塊中由 letconst 宣告的變數繫結到此塊,並且無法從外部訪問。

"use strict";
let a = 0;
// ...
if (a == 0) {
  let x = 5;
  alert(x);   // shows the number 5
} else {
  alert(x);   // ReferenceError (with a different 'a')
}
alert(x);     // ReferenceError

變數 x 在一個塊中宣告(在這個簡單的例子中,塊只包含兩行)。它在塊的末尾(即 else 行中的右花括號 })之後無法訪問。如果變數 xconst 而不是 let 宣告,也是如此。


小心使用關鍵字 var;它的語義不同! 首先,var 不是塊級作用域的。其次,它會導致一種稱為 提升 的技術,該技術從 JavaScript 的最初版本開始就被使用。提升會改變不同宣告的“幕後”語義。關於 var,它將宣告和初始化分成兩個獨立的語句,並將宣告部分移到當前作用域的頂部。因此,如果你在 **原始碼中** 宣告該變數之前使用它,則該變數將被宣告,但不會被初始化。

指令碼

"use strict";
alert(x);   // undefined, not ReferenceError !
x = 1;      // correct, despite of "use strict"
alert(x);   // shows 1
var x = 0;

將變為

"use strict";
var x;
alert(x);   // undefined, not ReferenceError !
x = 1;      
alert(x);   // shows 1
x = 0;

另一方面,關鍵字 let 會將宣告保留在它被寫入的行中。

"use strict";
alert(x);     // ReferenceError
x = 1;        // ReferenceError
alert(x);     // ReferenceError
let x = 0;

還有一些其他差異。以下是用 var 替換本章第一個例子中 let 的版本

"use strict";
let a = 0;
// ...
if (a == 0) {
  var x = 5;  // 'var' instead of 'let'
  alert(x);   // shows the number 5
} else {
  alert(x);   // ReferenceError (with a different 'a')
}
alert(x);     // shows the number 5  !!

我們建議完全避免使用 var,因為有兩個原因

  • JavaScript 的提升技術不容易理解。
  • C 家族的其他語言成員不知道它。

不要使用 var,而應該使用關鍵字 let

函式作用域

[edit | edit source]

函式建立自己的作用域。在函式作用域中宣告的變數無法從外部訪問。

"use strict";
function func_1() {
  let x = 5; // x can only be used in func_1
  alert("Inside function: " + x);
}
func_1();
alert(x); // Causes an error

函式作用域有時被稱為區域性作用域,因為這是舊版本 ECMAScript 中的名稱。

另請參閱:閉包 的工作方式相反——在函式內部訪問外部變數。

模組作用域

[edit | edit source]

可以將大型指令碼分成多個檔案,並讓函式和變數相互通訊。每個檔案建立自己的作用域,即模組作用域。章節JavaScript/Modules 對此進行了更多解釋。

全域性作用域

[edit | edit source]

如果變數或函式在指令碼的頂層(在所有塊和函式之外)宣告,則它們位於全域性作用域中。

"use strict";
let x = 42;   // 'x' belongs to global scope

// define a function
function func_1() {
  // use variable of the global context
  alert("In function: " + x);
}

// start the function
func_1();   // shows "In function: 42"
alert(x);   // shows "42"

x 在頂層宣告,因此它位於全域性作用域中,可以在任何地方使用。但在下一個示例中,x 的宣告被 { } 包裹。因此它不再位於全域性作用域中。

"use strict";
{
  let x = 42;   // 'x' is not in global scope
}
alert(x);       // ReferenceError

提示:使用全域性作用域不被認為是好的做法。它往往會產生不必要的副作用。相反,嘗試將程式碼模組化,並讓各個部分透過介面進行通訊。

練習

[edit | edit source]
... 可以在另一個頁面上找到(點選這裡)。

另請參閱

[edit | edit source]

參考資料

[edit | edit source]
華夏公益教科書