本篇内容:梳理var、let、const的用法及注意事项
原文链接: 点此查看
var
存在变量提升:只提升声明语句,不提升赋值语句
我们来看下面这段代码
var foo = {n:1};
(function(foo){
console.log(foo.n);
foo.n = 3;
var foo = {n:2};
console.log(foo.n);
}(foo);
console.log(foo.n);
这段代码输出的结果是
1
2
3
这是因为由于JavaScript是先编译后执行的,所以在编译阶段会先声明foo
,然后在执行到foo = {n:2}
的时候才对其赋值。引擎会将代码理解为以下格式:
var foo = {n:1};
(function(foo){ // 此时foo指向外部变量
var foo; // 变量提升,外部变量已声明,不做改变
console.log(foo.n); // 1
foo.n = 3; // 外部变量改变,n=3
foo = {n:2}; // foo指向改变为内部变量,并且n赋值为2
console.log(foo.n) // 函数内部变量,2
})(foo);
console.log(foo.n) // 3
先提升函数,后提升变量
思考以下代码:
foo(); // 输出1而不是2
var foo;
function foo() {
console.log(1);
}
foo = function() {
console.log(2);
}
原因是由于函数提升优于变量提升,引擎会将代码片理解为以下形式:
function foo() {
console.log(1);
}
/* var foo // 由于function foo被提升到了开头,使得foo以及被定义了,var foo失去了意义而被忽略 */
foo(); //1
foo = function() {
console.log(2);
}
let
不存在变量提升
变量提升往往会对开发造成困扰,幸好在ES6中引入了let语法。
let
不存在变量提升,其所声明的变量一定要在声明语句之后使用。
console.log(foo); // undefined
var foo = 2;
console.log(bar); // ReferenceError
let bar = 2;
块级作用域
let
关键字可以将其声明的变量绑定到所在的任意作用域中(通常是{…}内部),即隐式地创建一个块级作用域。在这个(块级)作用域中通过let
声明的变量只在存在于该作用域内,而不像var
那样在全局范围内有效。
{
let a = 10;
var b = 1;
}
a; // ReferenceError: a is not defined.
b; // 1
这样可以大大改善代码中由于全局变量而引发的错误,比如以下代码:
var a = [];
for(var i = 0; i < 10; i++){
a[i] = function() {
console.log(i);
};
}
a[6](); // 10
上述代码由于变量i
是用var
声明的,所以在全局范围有效,当循环体执行完时,i = 10
,当我们在外部调用时,console.log(i)
中的i实际上是指向全局变量i
的,于是输出为10。
让我们来看看使用let的情况:
var a = [];
for(let i = 0; i < 10; i++) {
a[i] = function() {
console.log(i);
};
}
a[6](); // 6
上述代码中,由于i
是由let
定义的,所以只在本轮for循环中有效,所以console.log(i)
中的i
指向的其实是10个不同的i
,所以在外部调用的时候,其结果就为该轮赋值给i的结果。
暂时性死区
只要块级作用域中存在let
命令,它说声明的变量就绑定在了这个区域,不受外部影响。
var tmp = 123;
if(true) {
tmp = 'abc'; // ReferenceError
let tmp;
tmp = 10;
console.log(tmp); // 10
}
console.log(tmp); // 123
上述代码中,虽然存在全局变量tmp
,但是在块级作用域内let
又生声明了一个同名的局部变量,此时在这个块级作用域内形成了封闭作用域,变量tmp
受let
管理,与全局变量独立开。
不允许重复声明
let
在相同作用域内不允许重复声明,而var
允许这么
做。
const
类似于C++,JS的
const
变量不得改变值
const
变量声明时必须赋值
与let
相同,const
变量只在说声明的块级作用域内有效