因此,第一个console.log会抛错,[Uncaught ReferenceError: Cannot access 'myname' before initialization]。抛错则函数会中断执行,为了能让我们的代码继续分析,我们先加个 try-catch ,然后继续分析:
function foo() {
var test = 1
let myname= 'LuckyWinty'
try{
{
console.log(myname)
let myname= 'winty'
}
}catch(ex){
console.error(ex)
}
console.log(test,'---',myname)
}
foo()
//思考一下会输出什么?
块级作用域就是通过词法环境的栈结构来实现的,而变量提升是通过变量环境来实现,通过这两者的结合,JavaScript 引擎也就同时支持了变量提升和块级作用域了。
词法环境跟函数上下文,都是通过栈结构实现的。函数内部通过 var 声明的变量,在编译阶段全都被存放到变量环境(函数上下文)中,而通过let和const申明的变量会被追加到词法环境中,当这个块执行结束之后,追加到词法作用域的内容又会销毁掉。
举个例子:
执行到第一个
console.log
前的执行上下文是这样的:从图中看,第一个
console.log
理论上应该输出undefined
。但是语法规定了一个"暂时性死区(TDZ,当进入它的作用域,它不能被访问(获取或设置)直到执行到达声明)"
,也就是说虽然通过let声明的变量已经在词法环境中了,但是在没有赋值之前,访问该变量JavaScript引擎就会抛出一个错误。因此,第一个
console.log
会抛错,[Uncaught ReferenceError: Cannot access 'myname' before initialization]。抛错则函数会中断执行,为了能让我们的代码继续分析,我们先加个 try-catch ,然后继续分析:执行到第二个
console.log
前的执行上下文是这样的:此时,
{}
块作用域中的内容已执行完毕,被销毁掉了。第二个console.log
会输出1 "---" "LuckyWinty"
。