跳轉到內容

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預定義了一個變數PIconst 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 行中的閉合花括號 }。如果變數 x 使用 const 而不是 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;

還有更多不同之處。以下是本章第一個例子的一個版本,將 let 替換為 var

"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]
華夏公益教科書