跳至內容

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行中的結束花括號}。同樣,變數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/模組對這方面進行了更多解釋。

全域性作用域

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