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 x = 0;
// ...
x = x + 5;
// ...
x = -4;
// ...
關鍵字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與let相同。但這些變數被知道的範圍與由var宣告的變數不同;請參閱下面有關作用域的章節。
你可以將值分配給變數,而無需事先宣告變數。 "JavaScript 以前允許對未宣告的變數進行賦值,這會建立一個未宣告的全域性變數。這在嚴格模式中是一個錯誤,應該完全避免。" [1]換句話說:變數進入全域性作用域,請參閱下面。只要你沒有充分的理由,你就應該避免使用全域性作用域,因為它的使用往往會導致意外的副作用。
// direct usage of 'radius' without any keyword for its declaration
/* 1 */ radius = 5;
/* 2 */ alert (2 * radius * 3.14);
有時這種情況是意外發生的。如果原始碼中存在拼寫錯誤,JavaScript 會使用兩個不同的變數:原始變數和一個具有錯誤拼寫名稱的新變數 - 即使你使用了一個let、const或var關鍵字。
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 瞭解四種作用域:塊作用域、函式作用域、模組作用域和全域性作用域。根據宣告的型別和宣告的位置,變數位於這些作用域內。它們只有在它們的作用域內才是 '可見的' 或 '可訪問的'。如果你嘗試從外部訪問它們,就會出現錯誤。
一對花括號{}建立一個塊。在塊內由let或const宣告的變數繫結到該塊,並且不能在塊外訪問。
"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;
還有更多差異。以下是用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
提示:不建議使用全域性作用域。它往往會產生不必要的副作用。相反,請嘗試對程式碼進行模組化,並讓各部分透過介面進行通訊。