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

本教程将介绍哪些变量,如何声明和命名它们,并仔细研究var,let和const之间的区别。它也检验了提升的效果以及全局和局部范围对变量行为的重要性。

介绍

变量是许多编程语言的基本组成部分,也是新手编程人员学习的第一个也是最重要的概念。 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);
}
true

如前所述,变量可以用来表示任何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);
300

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

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

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

console.log(password);
'hunter3'

在实际的程序中,密码很可能会安全地存储在数据库中。 然而,这个例子说明了我们可能需要更新变量值的情况。 password的值是hunter2 ,但是我们将它重新分配给了hunter3 ,这是JavaScript从这一点可以认识到的价值。

命名变量

变量名称在JavaScript中被称为标识符 我们在JavaScript理解语法和代码结构中讨论了几种命名标识符的规则,总结如下:

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

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

这可能看起来像许多要学习的规则,但它很快就会成为编写有效和常规变量名称的第二性质。

varletconst之间的区别

JavaScript有三个不同的关键字来声明一个变量,这为该语言增加了一层复杂的层次。 三者之间的差异取决于范围,提升和重新分配。

关键词 范围 吊装 可以重新分配 可以重新申报
var 功能范围
let 块范围 没有 没有
const 块范围 没有 没有 没有

你可能想知道你应该在自己的程序中使用哪三种。 一个普遍接受的做法是尽可能多地使用const ,并在循环和重新分配的情况下进行。 通常,在遗留代码上工作之外,可以避免使用var

变量范围

JavaScript中的范围是指代码的当前上下文,它决定了变量对JavaScript的可访问性。 这两种范围是本地全球的

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

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

// 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);
human
werewolf
human

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

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

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

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}.`);
It 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}.`);
It 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;
undefined

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

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

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

造成这种情况的原因是由于提起了一个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的输出为true ,而不是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();
undefined

在这个例子中,我们宣布x在全球范围内为100 根据if语句, x可以变为200 ,但由于条件为false它不应该影响x的值。 相反, 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();
true

重复声明变量,这可能与var ,将引发letconst的错误。

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

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

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

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

常量

许多编程语言都具有常量 ,这些常量是无法修改或更改的值。 在JavaScript中, const标识符是在常量之后建模的,并且分配给const的值不能被重新分配。

以大写形式写入所有const标识符是很常见的约定。 这标志着他们很容易区别于其他变量值。

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

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

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

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

由于const值不能被重新分配,他们需要同时声明和初始化,否则也会抛出错误。

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

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

在编程中不能改变的值被认为是不可变的 ,而可以改变的值是可变的 虽然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);
{ color: 'blue', price: 20000 }

常量对于向你的未来自己和其他程序员清楚地表明与你一起工作的项目非常有用,即不应该重新分配预期的变量。 如果您预计未来可能会修改某个变量,那么您可能希望使用let来声明该变量。

结论

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

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