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
提示:不建议使用全局作用域。它往往会产生不必要的副作用。相反,请尝试对代码进行模块化,并让各部分通过接口进行通信。