跳至内容

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