blog
blog copied to clipboard
ES6之变量和作用域
变量
变量是具有唯一名称的命名容器,用于存储数据值。 以下语句声明了一个名称为“name”的变量:
let name;
console.log(name); // undefined
在JavaScript中,变量在创建时被初始化为undefined。在声明变量时,可以使用赋值运算符(=)将值赋给变量:
let name = 'jiayi';
console.log(name); // "jiayi"
在使用变量之前一定要初始化它们,否则会出现错误:
console.log(name); // ReferenceError: name is not defined
let name = 'jiayi';
ECMAScript 2015(或ES6)引入了两种声明变量的新方法:let
和 const
。使用新关键字的原因是var
的函数作用域令人困惑。这是JavaScript bug的主要来源之一。
作用域
从范围上讲,我们正在讨论运行时代码不同区域中变量的可见性。换句话说,代码的哪些区域可以访问和修改变量。
在JavaScript中,作用域有两种:
- 全局作用域
- 局部作用域
现代JavaScript (ES6+)所改变的是我们在局部作用域中使用变量的方式。
全局作用域
在函数外部声明的变量成为全局变量。这意味着可以在代码中的任何地方访问和修改它。
我们可以在全局作用域中声明常量:
const COLS = 10;
const ROWS = 20;</span>
我们可以从代码的所有区域访问它们
局部作用域
在局部作用域中声明的变量不能从局部作用域中外部访问。相同的变量名可以在不同的作用域中使用,因为它们被绑定到各自的作用域中。
局部作用域因所使用的变量声明而不同。
函数作用域
用var
关键字声明的变量成为函数的局部变量。可以从函数内部访问它们。
function printColor() {
if(true) {
console.log(color); // undefined
var color = "pink";
console.log(color); // "pink"
}
console.log(color); // "pink"
}
printColor();
console.log(color); // ReferenceError: color is not defined
我们可以看到,即使我们在声明之前访问color
,也不会出错。使用var
关键字声明的变量会被提升到函数顶部,并在代码运行之前用undefined进行初始化。通过提升,即使在声明之前,也可以在其封闭范围内访问它们。
你能看出这是如何让人困惑的吗?
块级作用域
在ES6中引入了块作用域的概念,以及声明变量const
和let
的新方法。这意味着变量可以在两个大括号{}
之间访问。例如在if
或for
里面。
function printColor() {
if(true) {
console.log(color); // ReferenceError: Cannot access 'color' before initialization
let color = "pink";
console.log(color); // "pink"
}
console.log(color); // ReferenceError: color is not defined
}
printColor();
console.log(color); // ReferenceError: color is not defined
let
和const
变量只有在定义求值后才初始化。它们不会像函数范围中那样被提升。在初始化之前访问它们会导致ReferenceError
。
我希望你们能看到这是如何更容易推理的。
Const vs Let
因此,既然我们知道应该使用 let
和 const
,那么什么时候应该使用它呢?
两者的不同之处在于 const
的值不能通过重新赋值改变,也不能被重新声明。因此,如果我们不需要重新赋值,我们应该使用const
。这也使得代码更加清晰,因为我们用一个变量来表示一个不可变的值。
甚至可以始终将变量声明为const,直到看到需要重新分配变量然后更改为let为止。 何时需要让我们进入循环的一个示例:
for(let i = 0; i < 10; i++) {
console.log(i); // 1, 2, 3, 4 …
}
Var vs Let
我们为什么要使用let
,而不是var
。
我们写一个循环,最终符合预期输出:
for(var i = 0; i < 10; i++) {
console.log(i); // 1, 2, 3, 4 …
}
这是很常见的操作,看不出什么毛病,那么我们修改一下写法:
for(var i = 0; i < 10; i++) {
setTimeout(function(){
console.log(i); // 1, 2, 3, 4 …
}, 1000)
}
我们本以为得到期望输出,但是结果却是 10 个 10
。
为什么会这样呢。
-
for
循环会先执行完再执行setTimeout
回调函数(同步优先于异步优先于回调) -
for
循环和setTimeout
回调函数不在一个作用域。setTimeout
回调函数属于函数级的作用域,不属于for
循环体,属于全局。 - 等到
for
循环结束,i
已经等于10
了,这个时候再执行setTimeout
的五个回调函数,里面的i
去向上找作用域,只能找到全局下的i
,即10
。所以输出都是10
。
那么我们需要需要let
来救赎:
for(let i = 0; i < 10; i++) {
setTimeout(function(){
console.log(i); // 1, 2, 3, 4 …
}, 1000)
}
除此之外,也可以通过闭包来解决问题,这是一道常见的经典面试题
总结
- 声明在全局作用域中的变量在代码中的任何地方都可以访问
- 在局部作用域中声明的变量不能从局部作用域中外部访问
-
const
和let
使用存在于两个大括号{}
之间的块作用域 - 通常将
const
用于其值永远不会改变的变量 - 对于其他的申明使用
let
- 不要使用
var
以避免混淆