Skip to content

作用域和变量

作用域

是什么

在 JavaScript 中,作用域是一个非常重要的概念。作用域决定了变量、函数和对象的可访问性和生命周期。它控制了标识符(变量名、函数名等)的可见性和查找。

作用域类型

JavaScript 主要有两种类型的作用域:

  1. 全局作用域:在代码的任何地方都可以访问到的变量、函数等拥有全局作用域。例如,在浏览器环境中,全局作用域通常是 window 对象。

  2. 局部作用域:只在某个特定代码段内可以访问的变量、函数等拥有局部作用域。函数内部就是一个局部作用域,也称为函数作用域。

在 ES6 中,引入了一种新的作用域类型:

  1. 块级作用域:由花括号 {} 包围的代码块具有块级作用域。letconst 声明的变量就在块级作用域中。

以下是一个简单的例子来说明作用域的概念:

javascript
var a = 1; // a 在全局作用域中

function test() {
  var b = 2; // b 在 test 函数的局部作用域中
  console.log(a); // 可以访问全局作用域中的 a
  console.log(b); // 可以访问局部作用域中的 b
}

test();
console.log(a); // 可以访问全局作用域中的 a
console.log(b); // 错误:无法访问局部作用域中的 b

在这个例子中,变量 a 在全局作用域中,可以在任何地方访问。变量 btest 函数的局部作用域中,只能在 test 函数内部访问。当我们试图在 test 函数外部访问 b 时,JavaScript 会抛出一个错误,因为 b 在那个作用域中不存在。

总的来说,理解作用域对于编写和理解 JavaScript 代码非常重要,它帮助我们管理变量和函数,避免命名冲突,并提供了变量生命周期的控制。

变量

变量类型

在 JavaScript 中,你可以使用以下几种变量:

  1. var: 这是最早的 JavaScript 变量声明方式,它的特性包括函数作用域和变量提升(hoisting)。

  2. let: 在 ES6(即 ECMAScript 2015)中引入,let变量具有块级作用域,而不是var的函数作用域。这意味着let变量在声明它的块(或任何包含它的块)之外是不可见的。

  3. const: 同样在 ES6 中引入,const变量的行为类似于let变量,但是一旦赋值就不能改变。这并不意味着它是不可变的,但是你不能重新分配引用。

这三种变量类型的主要区别在于它们的作用域(scope)、提升(hoisting)行为,以及是否可以重新赋值(re-assignment)。

关于 JavaScript 变量,还有一些重要的知识点:

  1. 变量提升(Hoisting):在 JavaScript 中,变量和函数声明在编译阶段被"提升"到它们各自的作用域的顶部。这意味着你可以在声明之前使用变量和函数。但是,只有声明本身会被提升,初始化(如果存在)会保留在原地。对于var,提升意味着它会被提升到函数作用域的顶部,对于letconst,它们会被提升到块作用域的顶部,但是在声明之前的访问会导致一个暂时性死区(Temporal Dead Zone,TDZ)错误。

  2. 全局变量:在函数之外声明的变量是全局变量,它们可以在 JavaScript 代码的任何地方访问。在浏览器环境中,全局变量也是window对象的属性。

  3. 局部变量:在函数内部声明的变量是局部变量,它们只能在该函数内部访问。

  4. 块级作用域letconst关键字创建的变量具有块级作用域,这意味着它们只能在最近的一组花括号(通常是一个if语句或for循环)中访问。

  5. 变量命名规则:变量名称可以包含字母、数字、美元符号($)和下划线(_),但是不能以数字开头。此外,还有一些保留字(如varletconst等)不能用作变量名。

  6. 动态类型:JavaScript 是一种动态类型语言,这意味着你可以声明一个变量来存储某种类型的值,然后再将同一变量用于存储不同类型的值。

  7. null 和 undefined:在 JavaScript 中,null是一个表示无值或无对象的特殊值,它表示变量被赋予了"空"或"无"的值。undefined是变量未被赋值的状态。

  8. 严格模式:在严格模式下,必须明确使用varletconst来声明变量。否则,如果你试图使用未声明的变量,JavaScript 将抛出错误。你可以通过在脚本的顶部添加"use strict";来启用严格模式。

区别

var

var关键字声明的变量存在变量提升(hoisting)的特性,即不论var出现在哪个位置,都会被提升到其作用域的顶部。但是,只有声明会被提升,赋值不会。此外,var没有块级作用域,它的作用域是函数作用域。如果在函数外部声明,它就是全局变量。

例如:

javascript
console.log(x); // 输出:undefined
var x = 5;
console.log(x); // 输出:5

if (true) {
  var y = 10;
}
console.log(y); // 输出:10

在上述代码中,x在声明前就被引用,但由于变量提升,它的值是undefined而不是报错。y虽然在if块中声明,但由于var没有块级作用域,所以yif块外也可以访问。

let

let关键字声明的变量具有块级作用域,即只在声明它的代码块中有效。此外,let不会变量提升,如果在声明前就使用这个变量,JavaScript 会抛出一个错误。同时,let不允许在相同作用域内重复声明同一个变量。

例如:

javascript
if (true) {
  let z = 20;
}
console.log(z); // 报错:z is not defined

console.log(a); // 报错:Cannot access 'a' before initialization
let a = 5;

let b = 10;
let b = 20; // 报错:Identifier 'b' has already been declared

在上述代码中,zif块中使用let声明,因此在if块外无法访问z,所以会报错。变量a在声明前就被引用,由于let不会变量提升,所以会报错。最后,尝试用let重复声明变量b,也会报错。

总的来说,varlet的主要区别在于作用域和变量提升:var有函数作用域和变量提升,而let有块级作用域,没有变量提升,并且不允许重复声明。

hancenter808@outlook.com