在另一篇博文《JavaScript中的执行上下文、作用域链、原型链和闭包详解》中,讲解了执行上下文的概念以及通过一个例子讲解了用全局执行上下文和函数执行上下文,下面将这个例子的调用栈图再次放到这里:
var a = 10;
function add(b,c){
return b+c;
}
function sum(b,c){
var d = 2;
var result = add(b,c);
return a+result+d;
}
sum(3,4);
(1)创建全局执行上下文,压入调用栈栈底
(2)执行赋值操作:a=10
(3)调用sum函数,将sum函数的执行上下文入栈
(4)执行赋值操作:d=2
(5)在sum函数中调用add函数,将add函数的执行上下文入栈
(6)add函数调用结束,返回结果7赋值给result
(7)sum函数调用结束
(8)整个js代码执行结束
var定义的变量以及完整申明的函数提升之后会放到执行上下文中的变量环境对象中,那上图中的“词法环境”是什么呢?这个就是今天的重点,和存放用let或者const定义的变量有关。
ES6引入了let和const关键字使JavaScript有了块级作用域,JavaScript是如何管理函数级作用域变量和块级作用域变量的呢?下面通过一个例子解答这个疑问:
function foo(){
var a = 1;
let b = 2;
{
let b = 3;
var c = 4;
let d = 5;
console.log(a);
console.log(b);
}
console.log(b);
console.log(c);
console.log(d);
}
foo();
(1)当执行完let b=2;的时候
(2)当刚执行到块里边的时候
从上图可以看出,块外部的let变量和块内部的let变量在词法环境中我画了两个方块,实际上,词法环境也维护了一个小的栈结构,当进入一个作用域快之后,就会把该作用域快内部的变量压入栈顶,当作用域执行完后,该作用域的信息就会从栈顶弹出。这样,块内部的let变量就不会覆盖块外部的let变量(同const变量)。
(3)当执行完let d=5;的时候
(4)当块执行完后
当执行console.log(a)的时候,要在词法环境和变量环境中找变量a,先在词法环境中沿着栈顶查找,若存在则取这个值,若不存在再去变量环境中找。
最终结果:
实例
var myname= 'a';
{
console.log(myname);
var myname= 'b';
}
console.log(myname);
这段代码不存在块级作用域,myname变量在全局执行环境的上下文环境中,当执行完var myname= 'a';
后,打印myname的值肯定是a,当执行完var myname= 'b';
,myname的值被修改为b,再次打印myname的值为b。
let myname= 'a'
{
console.log(myname)
let myname= 'b'
}
console.log(myname);
而这段代码使用了块级作用域,当进入块的时候,会在词法环境中将该块级作用域的信息压入栈顶,myname词法环境栈顶区域的初始值为undefined,试图去打印myname的值会报错:
由此可见,访问未初始化的块级作用域变量是会报错的。