浅层理解:
- let 声明的变量的作用域是块级的;
- let 不能重复声明已存在的变量;
- let 有暂时死区,不会被提升。
但是下面的例子:
var liList = document.querySelectorAll('li') // 共5个li
for( var i=0; i<liList.length; i++){
liList[i].onclick = function(){
console.log(i)
}
}
//依次打印5个5,for循环的小括号是一个作用域,而花括号又是一个作用域,而小括号的作用域是包裹住了大括号作用域的。所以循环结束此时的i为5,执行完循环再执行下面的内容。
var liList = document.querySelectorAll('li') // 共5个li
for( let i=0; i<liList.length; i++){
liList[i].onclick = function(){
console.log(i)
}
}
//分别打印出 0、1、2、3、4
//在每次执行循环体之前,JS 引擎会把i在循环体的上下文中重新声明及初始化一次。
开始思考变量提升问题:
- JavaScript 只有声明的变量会提升,不会提升初始化。
var num=10;
function fn(){
console.log(num);
var num = 20;
}
//输出undefined,首先num=10加载到window,然后执行函数fn,找到变量num提升,但是20初始化不能提升,导致输出num时没有赋值。
var
存在变量提升let
也存在变量提升,但是提升后会产生一个临时死区(TDZ)(即不能在初始化之前,使用变量),导致无法访问该变量(即使该变量已经存在)。因此会让我们误以为let
不能变量提升。
把JS变量拆分成三个部分:创建create(声明declare)、初始化initialize(绑定binding)、赋值assign,加深理解。
var声明:
function fn(){
var x = 1
var y = 2
}
fn()
执行以上代码,有以下过程:
- 进入 fn,为 fn 创建一个环境;
- 找到 fn 中所有用 var 声明的变量,在这个环境中「创建」这些变量(即 x 和 y);
- 将这些变量「初始化」为 undefined;
- 开始执行代码;
- x = 1 将 x 变量「赋值」为 1;
- y = 2 将 y 变量「赋值」为 2。
函数声明:
fn2()
function fn2(){
console.log(2)
}
JS 引擎会有以下过程:
- 找到所有用 function 声明的变量,在环境中「创建」这些变量;
- 将这些变量「初始化」并「赋值」为 function(){ console.log(2) };
- 开始执行代码 fn2()。
let声明:
{
let x = 1;
x = 2;
}
只看当前块级作用域:
- 找到所有用 let 声明的变量,在环境中「创建」这些变量
- 开始执行代码(注意现在还没有初始化)
- 执行 x = 1,将 x 「初始化」为 1(这并不是一次赋值,如果代码是 let x,就将 x 初始化为 undefined)
- 执行 x = 2,对 x 进行「赋值」
如果在let x 前使用x会报错:
let x = 2
{
console.log(x);// Uncaught ReferenceError: x is not defined
let x = 1;
}
- console.log(x) 中的 x 指的是下面的 x,而不是全局的 x
- 执行 log 时 x 还没「初始化」,所以不能使用(也就是所谓的暂时死区)
参考链接1 https://zhuanlan.zhihu.com/p/28140450
参考链接2 https://juejin.cn/post/6844903717594988557
参考链接3 https://www.jianshu.com/p/0f49c88cf169