变量、作用域、闭包

来自Wikioe
Eijux讨论 | 贡献2023年4月15日 (六) 20:00的版本
跳到导航 跳到搜索


变量

ECMAScript 6 中引入了 let、const。

JavaScript 有三种声明方式:

  1. var:声明一个变量局部变量 / 全局变量),初始化可选。
  2. let:声明一个块作用域局部变量,初始化可选。
  3. const:声明一个块作用域只读常量,必须初始化。
局部变量 / 全局变量:只与其定义位置有关。

作用域

ECMAScript 6 之前没有“块作用域”:语句块中声明的变量将成为语句块所在函数(或全局作用域)的局部变量。
  1. 全局作用域:可被当前文档中的任何其他代码所访问;
    “全局变量”的作用域
  2. 函数作用域:可被当前函数内部的其他代码所访问;
    “局部变量”的作用域
  3. 块作用域:可被当前(由 { } 包围)内部的其他代码所访问;
    “局部变量”的作用域
var 变量没有“块作用域”:将会使用(块所在的)“函数作用域”或“全局作用域”

var 变量将透传 if、for 和其它代码块 —— 这是因为在早期的 JavaScript 中,块没有词法环境

示例:

if (true) {
    var x = 5;
}
console.log(x); // 5

if (true) {
    let y = 5;
}
console.log(y); // ReferenceError: y is not defined

变量提升

词法作用域

闭包

一个有意思的例子

一个涉及到“Event Loop”和“var 作用域”的例子。

对于以下代码:

for (var i = 0; i < 10; i++) {
    setTimeout(function() { console.log(i) }, 100 * i)
}
是否预期输出:
0
1
2
3
4
5
6
7
8
9
但,实际输出:
10
10
10
10
10
10
10
10
10
10

其原因有两点:

  1. setTimeout 是一个任务,将在 for 所有循环完成之后执行;
  2. i 由 var 定义:所有的 setTimeout 实际上都引用了“相同作用域里的同一个 i” —— 而它在所有循环之后其值为 10;

要与预期输出一致,有两种方式:

  1. 通过使用“立即执行的函数表达式”来捕获 i:
    for (var i = 0; i < 10; i++) {
        (function(i) {
            setTimeout(function() { console.log(i) }, 100 * i)
        })(i)
    }
    
    • 不能妄想通过 let、const 来捕获:
      for (var i = 0; i < 10; i++) {
          setTimeout(function() { const v = i; console.log(i); }, 100 * i)
      }
      
  2. 使用 let 替换 var
    for (let i = 0; i < 10; i++) {
        (function(i) {
            setTimeout(function() { console.log(i) }, 100 * i)
        })(i)
    }
    
var作用域或函数作用域

当用let声明一个变量,它使用的是词法作用域或块作用域。

参考