跳转到内容

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]
华夏公益教科书