了解JavaScript中的变量、范围和提升

来自菜鸟教程
跳转至:导航、​搜索

介绍

变量 是许多编程语言的基本组成部分,是新手程序员学习的首要和最重要的概念之一。 JavaScript中有许多不同的变量属性,以及命名它们时必须遵循的一些规则。 在 JavaScript 中,有三个关键字用于声明变量——varletconst——每个关键字都会影响代码以不同方式解释变量的方式。

本教程将介绍什么是变量、如何声明和命名它们,并详细了解 varletconst 之间的区别。 我们还将回顾提升的影响以及全局和局部范围对变量行为的重要性。

理解变量

变量 是用于存储值的命名容器。 我们可能多次引用的一条信息可以存储在一个变量中以供以后使用或修改。 在 JavaScript 中,变量中包含的值可以是任何 JavaScript 数据类型 ,包括数字、字符串或对象。

在今天的 JavaScript 所基于的 ECMAScript 2015 (ES6) 语言规范之前,只有一种声明变量的方法——使用 var 关键字。 因此,大多数较旧的代码和学习资源只会将 var 用于变量。 我们将在下面的 自己的部分 中讨论 varletconst 关键字之间的区别。

我们可以使用 var 来演示变量本身的概念。 在下面的示例中,我们将 声明 一个变量,并 为其赋值

// Assign the string value Sammy to the username identifier
var username = "sammy_shark";

该声明由几个部分组成:

  • 使用 var 关键字声明变量
  • 变量名(或标识符),username
  • 赋值操作,由 = 语法表示
  • 分配的值,"sammy_shark"

现在我们可以在代码中使用 username 了。 JavaScript 会记住 username 代表字符串值 sammy_shark

// Check if variable is equal to value
if (username === "sammy_shark") {
  console.log(true);
}
Outputtrue

如前所述,变量可用于表示任何 JavaScript 数据类型。 在此示例中,我们将使用字符串、数字、对象、布尔值和空值声明变量。

// Assignment of various variables
var name = "Sammy";
var spartans = 300;
var kingdoms = [ "mammals", "birds", "fish" ];
var poem = { roses: "red", violets: "blue" }; 
var success = true;
var nothing = null;

使用 console.log,我们可以看到特定变量中包含的值。

// Send spartans variable to the console
console.log(spartans);
Output300

变量将数据存储在内存中,以后可以访问和修改。 也可以重新分配变量并赋予新值。 下面的简化示例演示了如何将密码存储到变量中然后进行更新。

// Assign value to password variable
var password = "hunter2";

// Reassign variable value with a new value
password = "hunter3";

console.log(password);
Output'hunter3'

在实际程序中,密码很可能安全地存储在数据库中。 但是,此示例说明了我们可能需要更新变量值的情况。 password 的值是 hunter2,但我们将它重新分配给 hunter3,这是 JavaScript 从那时起识别的值。

命名变量

变量名称在 JavaScript 中称为 标识符 。 我们在 Understanding Syntax and Code Structure in JavaScript 中讨论了几个命名标识符的规则,总结如下:

  • 变量名只能由字母(a-z)、数字(0-9)、美元符号($)和下划线(_)组成
  • 变量名不能包含任何空白字符(制表符或空格)
  • 数字不能以任何变量的名称开头
  • 有几个保留关键字不能作为变量名
  • 变量名区分大小写

JavaScript 也有在使用 varlet 声明的函数和变量的名称中使用驼峰式大小写(有时被风格化为 camelCase)的约定。 这是将第一个单词小写,然后将每个后续单词的第一个字母大写的做法,它们之间没有空格。 大多数不是常量的变量都会遵循这个约定,但也有一些例外。 使用 const 关键字声明的常量变量的名称通常全部大写。

这似乎需要学习很多规则,但它很快就会成为编写有效和常规变量名的第二天性。

varletconst 的区别

JavaScript 有三个不同的关键字来声明一个变量,这给语言增加了一层额外的复杂性。 三者之间的区别在于范围、提升和重新分配。

关键词 范围 吊装 可以重新分配 可以重新声明
var 功能范围 是的 是的 是的
let 块范围 是的
const 块范围

您可能想知道应该在自己的程序中使用这三个中的哪一个。 一种普遍接受的做法是尽可能使用 const,在循环和重新分配的情况下使用 let。 通常,除了处理遗留代码之外,可以避免使用 var

变量范围

JavaScript 中的 Scope 是指代码的当前上下文,它决定了变量对 JavaScript 的可访问性。 两种作用域是 localglobal

  • 全局变量是在块外声明的变量
  • 局部变量是在块内声明的变量

在下面的示例中,我们将创建一个全局变量。

// Initialize a global variable
var creature = "wolf";

我们了解到可以重新分配变量。 使用局部作用域,我们实际上可以创建与外部作用域中的变量同名的新变量,而无需更改或重新分配原始值。

在下面的示例中,我们将创建一个全局 species 变量。 函数内部是一个同名的局部变量。 通过将它们发送到控制台,我们可以看到变量的值如何根据作用域不同而不同,并且原始值没有改变。

// Initialize a global variable
var species = "human";
 
function transform() {
  // Initialize a local, function-scoped variable
  var species = "werewolf";
  console.log(species);
}

// Log the global and local variable
console.log(species);
transform();
console.log(species);
Outputhuman
werewolf
human

在这个例子中,局部变量是 function-scoped。 使用 var 关键字声明的变量始终是函数范围的,这意味着它们将函数识别为具有单独的范围。 因此,无法从全局范围访问此局部范围的变量。

然而,新的关键字 letconst块作用域 。 这意味着从任何类型的块创建一个新的本地范围,包括功能块、if 语句和 forwhile 循环。

为了说明函数范围变量和块范围变量之间的区别,我们将使用 letif 块中分配一个新变量。

var fullMoon = true;

// Initialize a global variable
let species = "human";

if (fullMoon) {
  // Initialize a block-scoped variable
  let species = "werewolf";
  console.log(`It is a full moon. Lupin is currently a ${species}.`);
}

console.log(`It is not a full moon. Lupin is currently a ${species}.`);
OutputIt is a full moon. Lupin is currently a werewolf.
It is not a full moon. Lupin is currently a human.

在此示例中,species 变量具有一个全局值 (human) 和另一个本地值 (werewolf)。 但是,如果我们使用 var,将会有不同的结果。

// Use var to initialize a variable
var species = "human";

if (fullMoon) {
  // Attempt to create a new variable in a block
  var species = "werewolf";
  console.log(`It is a full moon. Lupin is currently a ${species}.`);
}

console.log(`It is not a full moon. Lupin is currently a ${species}.`);
OutputIt is a full moon. Lupin is currently a werewolf.
It is not a full moon. Lupin is currently a werewolf.

在此示例的结果中,全局变量和块范围变量都以相同的值结束,werewolf。 这是因为不是使用 var 创建新的局部变量,而是在同一范围内重新分配相同的变量。 var 不承认 if 是不同的新范围的一部分。 通常建议您声明块范围的变量,因为它们生成的代码不太可能无意中覆盖变量值。

吊装

到目前为止,在大多数示例中,我们使用 var声明 一个变量,并且我们用一个值 初始化 它。 声明和初始化后,我们可以访问或重新分配变量。

如果我们在声明和初始化之前尝试使用变量,它将返回 undefined

// Attempt to use a variable before declaring it
console.log(x);

// Variable assignment
var x = 100;
Outputundefined

但是,如果我们省略 var 关键字,我们就不再声明变量,只是初始化它。 它将返回 ReferenceError 并停止脚本的执行。

// Attempt to use a variable before declaring it
console.log(x);

// Variable assignment without var
x = 100;
OutputReferenceError: x is not defined

原因是 hoisting,这是 JavaScript 的一种行为,其中变量和函数声明被移动到其作用域的顶部。 由于只提升了实际声明,而不是初始化,因此第一个示例中的值返回 undefined

为了更清楚地展示这个概念,下面是我们编写的代码以及 JavaScript 是如何实际解释它的。

// The code we wrote
console.log(x);
var x = 100;

// How JavaScript interpreted it
var x;
console.log(x);
x = 100;

JavaScript 在执行脚本之前将 x 作为变量保存到内存中。 由于它在定义之前仍然被调用,所以结果是 undefined 而不是 100。 但是,它不会导致 ReferenceError 并停止脚本。 尽管 var 关键字实际上并没有改变 var 的位置,但这有助于说明提升的工作原理。 但是,此行为可能会导致问题,因为编写此代码的程序员可能期望 x 的输出为 100,而实际上是 undefined

在下一个示例中,我们还可以看到提升如何导致不可预测的结果:

// Initialize x in the global scope
var x = 100;

function hoist() {
  // A condition that should not affect the outcome of the code
  if (false) {
    var x = 200;
  }
  console.log(x);
}

hoist();
Outputundefined

在此示例中,我们将 x 声明为全局 100。 根据 if 语句,x 可以更改为 200,但由于条件是 false 它不应该影响 [ X157X]。 相反,x 被提升到 hoist() 函数的顶部,并且值变成了 undefined

这种不可预测的行为可能会导致程序中的错误。 由于 letconst 是块范围的,它们不会以这种方式提升,如下所示。

// Initialize x in the global scope
let x = true;

function hoist() {
  // Initialize x in the function scope
  if (3 === 4) {
    let x = false;
  }
  console.log(x);
}

hoist();
Outputtrue

使用 var 可能会重复声明变量,但会在 letconst 中引发错误。

// Attempt to overwrite a variable declared with var
var x = 1;
var x = 2;

console.log(x);
Output2
// Attempt to overwrite a variable declared with let
let y = 1;
let y = 2;

console.log(y);
OutputUncaught SyntaxError: Identifier 'y' has already been declared

总而言之,使用 var 引入的变量可能会受到提升的影响,提升是 JavaScript 中的一种机制,其中变量声明被保存到内存中。 这可能会导致代码中出现未定义的变量。 letconst 的引入通过在声明变量之前尝试使用变量或尝试多次声明变量时抛出错误来解决此问题。

常数

许多编程语言都具有 常量,它们是无法修改或更改的值。 在 JavaScript 中,const 标识符是根据常量建模的,分配给 const 的值不能重新分配。

通常将所有 const 标识符写成大写。 这标志着它们很容易与其他变量值区分开来。

在下面的示例中,我们使用 const 关键字将变量 SPECIES 初始化为常量。 尝试重新分配变量将导致错误。

// Assign value to const
const SPECIES = "human"; 

// Attempt to reassign value
SPECIES = "werewolf";

console.log(SPECIES);
OutputUncaught TypeError: Assignment to constant variable.

由于const值不能重新赋值,需要同时声明和初始化,否则也会报错。

// Declare but do not initialize a const
const TODO;

console.log(TODO);
OutputUncaught SyntaxError: Missing initializer in const declaration

在编程中不能改变的值称为immutable,而可以改变的值为mutable。 尽管 const 值不能重新分配,但它们是可变的,因为可以修改用 const 声明的对象的属性。

// Create a CAR object with two properties
const CAR = {
    color: "blue",
    price: 15000
}

// Modify a property of CAR
CAR.price = 20000;

console.log(CAR);
Output{ color: 'blue', price: 20000 }

常量有助于让您未来的自己和与您一起进行项目的其他程序员清楚不应重新分配预期的变量。 如果您希望将来可能修改变量,您可能希望使用 let 来声明该变量。

结论

在本教程中,我们了解了变量是什么、变量命名规则以及如何重新分配变量值。 我们还了解了范围和提升,原始 var 关键字的一些限制,以及 letconst 如何纠正这些问题。

要比较变量在其他语言中的使用方式,您可以阅读我们关于“如何在 Python 3 中使用变量”的教程。