结合JavaScript编译原理谈谈为什么let/const能实现块级作用域和阻止变量提升

let/const能实现块级作用域和阻止变量提升
先来一道“开胃菜“:

console.log(name1);
var name1 = "demo1";
console.log(name2);
let name2 = "demo2";

来,控制台要输出什么?

很显然,控制台将第一次输出undefined。

第一次非常容易理解:使用var 声明的变量会变量提升,控制台输出将在变量赋值之前,你可以理解成是下面这样的代码:

var name1 ;
console.log(name1);
name1 = "demo1";

第二次将直接报错,原因是es6的语法当中,let声明的变量在未声明前不允许使用,即“暂时性死区”。

这里就不解释了,比较简单且基础,但是我还是想额外地说明一下这样设计本身的缺陷性。

对这个点不感兴趣的读者,可以跳过,直接进入标题二:

1.var变量声明导致变量提升暗藏的语法缺陷

我还是用代码来说明JavaScript在变量和作用域设计上的缺陷性(尽管这并不影响JavaScript在我心目中的重要性)

var num = 1;
function sayNum(){
  var num = 2;
  if(true){
     var num = 3;
     console.log(num)
  }
  console.log(num)
}
sayNum();

好,现在提问,控制台2次分别输出什么?

毫无疑问,两次都应该是3。

在我写下这段代码的时候,我实际上是希望它能在第1次输出3,第2次输出2。

然而,JavaScript是一门没有块级作用域的语言,这样的缺陷会导致if代码块内声明的变量提升到函数作用域内。

这也就是为什么在es6语法中,要引入块级作用域的原因:变量在没有声明之前就被访问,这在编程语言当中是不合理的,同时也是难以被理解的一个逻辑。

2.从JavaScript的编译原理来谈谈const/let

尽管es6语法引入了let/const,让开发者可以解决上面的代码导致的缺陷,但是,JavaScript本身仍然保留着变量提升的特色

这在我学习的途中常常使我感到迷惑:既要保留变量提升的“缺陷”,又要修补和避开它,这是为什么?

这就需要了解JavaScript的编译原理。

还是用代码说明:

function sayNum(){
  var num1 = 1;
  let num2 = 2;
  {
     var num3 = 3;
     let num4 = 4;
  }
}
 sayNum();

JavaScript是一门“解释执行”的语言,代码在执行的时候,会预先经过一段“编译”的过程。

在编译阶段:

使用var声明的变量会进入变量环境,而使用const/let声明的变量会进入词法环境

3.let/const怎样实现块级作用域

回归代码,我来结合词法环境和变量环境来谈谈let/const怎样实现块级作用域的。

const sayNum = () => {
   const num1 = 1;
   var num2 = 2;
   {
     var num3 = 3;
     const num4 = 4;
     {
       var num5 = 5;
       const num6 = 6;
     }
   }
   console.log(num1);
   console.log(num2);
   console.log(num3);
   console.log(num4);
   console.log(num5); 
   console.log(num6);
}
sayNum();

假设我们在这里忽略JavaScript的单线程原理,然后我们来看下控制台会打印什么。

按照顺序如下:

   console.log(num1); //1
   console.log(num2); //2
   console.log(num3); //3
   console.log(num4); //报错,变量未声明
   console.log(num5); //5
   console.log(num6); //报错,变量未声明

在编译阶段,执行上下文如下:
在这里插入图片描述

编译阶段

在编译阶段,通过var声明的变量,会提升到函数变量环境中。

而let/const声明的变量,会被推入词法环境中。

在代码块内,通过let/const声明的变量,既不会推入变量环境,也不会推入词法环境。

现在我们会看代码,当代码执行到代码块时,词法环境会推入一个小型栈结构,如下所示:

在这里插入图片描述

执行到代码块时候的执行上下文

这个时候,词法环境会形成一个小型的栈结构,当前代码块内的let/const生命的变量,会遵循“后进先出”的原则被推入首位,查询变量的时候,会先从栈的首位开始查找。

栈的每个结构之间相互独立,不受影响。

同样的道理,在执行到下一个代码块的时候,也会出现代码块中的let/const声明的变量被推入栈的情况。

当作用域被执行完后,该作用域的信息会被从栈顶依次弹出,进而我们无法在代码块外访问到我们在代码块内声明的变量,因而这就是let/const命令能形成块级作用域的原因

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值