
开聊吧!(前面一小部分大佬们可以自动忽略)
首先,打脸我的无知!
在刚接触ES6的时候,我就「以为」我理解了let。然后漫长的自学道路上,let一次又一次的让我认识到了自己的无知。
希望写了这篇文章之后能用我的无知,让在这条道路上的人能少踩些坑。
初识let
和很多人一样,在听说了ES6很好用之后,就马不停蹄的去学习这门充满着语法糖的东西。开始抱着emmmm快速把es6过一遍的念头去了菜鸟教程(因为觉得这里的教程很简洁,但是希望大家以后慎重选择初学教程!!!)

let浅解
《1》let 声明的变量的作用域是块级的;
{
let a = 10;
var b = 1;
}
a // ReferenceError: a is not defined.
b // 1
用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效.
说说这个「块级作用域」里面的坑吧!!!
首先,用阮大神的一个示例来说说过第一个坑
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6]();
输出几呢?是6吗? 答案是10
为什么呢?因为上面代码中,变量i是var命令声明的,在全局范围内都有效,所以全局只有一个变量i。每一次循环,变量i的值都会发生改变,而循环内被赋给数组a的函数内部的console.log(i),里面的i指向的就是全局的i。也就是说,所有数组a的成员里面的i,指向的都是同一个i,导致运行时输出的是最后一轮的i的值,也就是 10。
那么怎么解决呢? ES6之前的解决方式--构造闭包形成不销毁的作用域保存变量
var a = [];
for (var i = 0; i < 10; i++) {
(function (arg) {a[i] = function () {
console.log(arg);
}})(i);
}
a[6](); //6
ES6的解决方式
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 6
代码中,变量i是let声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,所以最后输出的是6。 用ES5的代码描述就是这样的!
"use strict";
var a = [];
var _loop = function _loop(i) {
a[i] = function () {
console.log(i);
};
};
for (var i = 0; i < 10; i++) {
_loop(i);
}
a[6](); // 6
再来一个平常码代码经常会遇到的,面试也会经常遇到。
var liList = document.querySelectorAll('li') // 共5个li
for( var i=0; i<liList.length; i++){
liList[i].onclick = function(){
console.log(i)
}
}
打印多少呢?0,1,2,3,4 还是 5,5,5,5,5? 相信大家大家应该都知道依次点击 li 会打印出 5 个 5。
如果把 var i 改成 let i,就会分别打印出 0、1、2、3、4:
var liList = document.querySelectorAll('li') // 共5个li
for( let i=0; i<liList.length; i++){
liList[i].onclick = function(){
console.log(i)
}
}
阐述一下我的理解吧!
- for( let i = 0; i< 5; i++) 这句话的圆括号之间,有一个隐藏的作用域。
- for( let i = 0; i< 5; i++) { 循环体 } 在每次执行循环体之前,JS 引擎会把 i 在循环体的上下文中重新声明及初始化一次。
- 用ES5的代码描述就是这样的!
'use strict';
var liList = document.querySelectorAll('li'); // 共5个li
var _loop = function _loop(i) {
liList[i].onclick = function () {
console.log(i);
};
};
for (var i = 0; i < liList.length; i++) {
_loop(i);
}
总结:希望大家在已有遇到跟循环 异步事件之类的问题的时候多注意下,不要掉坑里了☺
《2》let 声明的变量不存在变量提升,有暂时性死区;
先来解释解释这两个吧
变量提升
console.log(foo); // 输出undefined
console.log(bar); // 报错ReferenceError
var foo = 2;
let bar = 2;
let不像var那样会发生“变量提升”现象。所以,变量一定要在声明后使用,否则报错。
暂时性死区
let x = 'global'
{
console.log(x) // Uncaught ReferenceError: x is not defined
let x = 1
}
存在全局变量x,但是块级作用域内let又声明了一个局部变量x,导致后者绑定这个块级作用域,所以在let声明变量前,对tmp赋值会报错。
在详细的解释这个问题之前,我们先来弄懂ES6之前,我们的作用域里的变量预解释。
var a = 1;
function b(params) {
var c = 2;
console.log(params);
}
console.log(a);
b(3);

b(3);
function b(params) {
var c = 2;
console.log(params);
}
然后就是函数执行的第二步了

这个我们弄清楚了 就再把let的说一遍 那么上面的问题你自然就豁然开朗了。
关于let的是这样的
<1>找到所有用 let 声明的变量,在环境中「创建」这些变量
<2>开始执行代码(注意现在还没有初始化也没有假装赋值为undefined)
<3>执行 let声明的变量时,将 该变量 「初始化直接赋值」
现在关于上面的不存在变量提升,有暂时性死区你应该都懂了吧。关于暂时性死区是因为let声明的变量有块级作用域而且没有变量提升所以就产生了
也来看看暂时性死区在es6之前的环境里是怎样的吧。
//es6中
let x = 'global'
{
console.log(x) // Uncaught ReferenceError: x is not defined
let x = 1
}
//之前版本的环境中
'use strict';
var x = 'global';
{
console.log(_x); // Uncaught ReferenceError: x is not defined
var _x = 1;
}
《3》let 不能重复声明已存在的变量
这个知识点就基本没啥坑了 自己注意点儿就好
// 报错
function () {
let a = 10;
var a = 1;
}
// 报错
function () {
let a = 10;
let a = 1;
}
总结
总共也就三个知识点:完全掌握还是要多用!!!
1、let的块级作用域!
2、let声明的变量不存在变量提升,有暂时性死区!
3、let声明的变量不允许重复声明,无论重复用var或者其他声明都不行!
let的故事就这样暂时说完啦(疯狂求有位大佬来带带我啊) 不知不觉又晚上10点多了 该回寝室了 哈哈 如果学到了就给我个小心心吧 哈哈哈
