您的位置:首页 > 其它

let与var的区别

2017-12-11 19:18 218 查看

let
语句声明一个块级作用域的本地变量,并且可选的将其初始化为一个值。

语法

let var1 [= value1] [, var2 [= value2]] [, ..., varN [= valueN]];

参数

var1
,
var2
, …,
varN
变量名。可以是任意合法的标识符。
value1
,
value2
, …,
valueN
变量的初始值。可以是任意合法的表达式。

描述

let
允许你声明一个作用域被限制在块级中的变量、语句或者表达式。与var关键字不同的是,它声明的变量只能是全局或者整个函数块的。

这里解释了我们为什么选取“let”这个名字。

作用域规则

let
声明的变量只在其声明的块或子块中可用,这一点,与
var
相似。二者之间最主要的区别在于
var
声明的变量的作用域是整个封闭函数。

function varTest() {
var x = 1;
if (true) {
var x = 2;  // 同样的变量!
console.log(x);  // 2
}
console.log(x);  // 2
}

function letTest() {
let x = 1;
if (true) {
let x = 2;  // 不同的变量
console.log(x);  // 2
}
console.log(x);  // 1
}

简化内部函数代码

当用到内部函数的时候,
let
会让你的代码更加简洁。

var list = document.getElementById('list');

for (let i = 1; i <= 5; i++) {
let item = document.createElement('li');
item.appendChild(document.createTextNode('Item ' + i));

item.onclick = function(ev) {
console.log('Item ' + i + ' is clicked.');
};
list.appendChild(item);
}

// to achieve the same effect with 'var'
// you have to create a different context
// using a closure to preserve the value
for (var i = 1; i <= 5; i++) {
var item = document.createElement('li');
item.appendChild(document.createTextNode('Item ' + i));

(function(i){
item.onclick = function(ev) {
console.log('Item ' + i + ' is clicked.');
};
})(i);
list.appendChild(item);
}

以上示例的工作原理是因为(匿名)内部函数的五个实例引用了变量
i
的五个不同实例。注意,如果你将
let
替换为
var
,则它将无法正常工作,因为所有内部函数都将返回相同的
i
:6的最终值。此外,我们可以通过将创建新元素的代码移动到每个循环的作用域来保持循环更清晰。

在程序或者函数的顶层,
let
并不会像
var
一样在全局对象上创造一个属性,比如:

var x = 'global';
let y = 'global';
console.log(this.x); // "global"
console.log(this.y); // undefined

模仿私有接口

在处理构造函数的时候,可以通过
let
绑定来共享一个或多个私有成员,而不使用闭包

var Thing;

{
let privateScope = new WeakMap();
let counter = 0;

Thing = function() {
this.someProperty = 'foo';

privateScope.set(this, {
hidden: ++counter,
});
};

Thing.prototype.showPublic = function() {
return this.someProperty;
};

Thing.prototype.showPrivate = function() {
return privateScope.get(this).hidden;
};
}

console.log(typeof privateScope);
// "undefined"

var thing = new Thing();

console.log(thing);
// Thing {someProperty: "foo"}

thing.showPublic();
// "foo"

thing.showPrivate();
// 1

let
暂存死区的错误

在相同的函数或块作用域内重新声明同一个变量会引发
SyntaxError


if (x) {
let foo;
let foo; // TypeError thrown.
}

在 ECMAScript 2015 中,
let
绑定不受变量提升的约束,这意味着
let
声明不会被提升到当前执行上下文的顶部。在块中的变量初始化之前,引用它将会导致
ReferenceError
(而使用
var 声明变量则恰恰相反,该变量的值是 undefined )。该变量处于从块开始到初始化处理的“暂存死区”。

function do_something() {
console.log(bar); // undefined
console.log(foo); // ReferenceError: foo is not defined
var bar = 1;
let foo = 2;
}

在 
switch
 声明中你可能会遇到这样的错误,因为它只有一个块.

switch (x) {
case 0:
let foo;
break;

case 1:
let foo; // TypeError for redeclaration.
break;
}

但是,重要的是要指出嵌套在case子句内的块将创建一个新的块作用域的词法环境,这不会产生上面显示的重新声明错误。

let x = 1;

switch(x) {
case 0: {
let foo;
break;
}
case 1: {
let foo;
break;
}
}

与词法作用域结合的暂存死区

由于词法作用域,表达式
(foo + 55)
内的标识符“foo”会解析为if块的foo,而不是覆盖值为33的foo。在这一行中,if块的“foo”已经在词法环境中创建,但尚未达到(并终止)其初始化(这是语句本身的一部分):它仍处于暂存死区。

function test(){
var foo = 33;
if (true) {
let foo = (foo + 55); // ReferenceError
}
}
test();

这种现象可能会使您陷入以下情况。指令
let n of n.a
已经在for循环块的私有范围内,因此标识符“n.a”被解析为位于指令本身的第一部分(“let n”)中的'n'对象的属性'a' ,由于尚未达成和终止其声明,因此仍处于暂存死区。

function go(n) {
// n here is defined!
console.log(n); // Object {a: [1,2,3]}

for (let n of n.a) { // ReferenceError
console.log(n);
}
}

go({a: [1, 2, 3]});

其他情况

当在块中使用时,
let
将变量的作用域限制为该块。注意
var
的作用域在它被声明的函数内的区别。

var a = 1;
var b = 2;

if (a === 1) {
var a = 11; // the scope is global
let b = 22; // the scope is inside the if-block

console.log(a);  // 11
console.log(b);  // 22
}

console.log(a); // 11
console.log(b); // 2
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: