这段JS代码你真的知道应该打印什么吗

229 阅读5分钟

这段JS代码你真的知道应该打印什么吗

今天在群里看到一段代码,请问如下代码应该打印什么?

var a=1999;	
{	
 a=2020;
 function a(){
 }
 a=2021;
}
console.log(a);	// 应该打印什么

第一反应:由于括号中定义了函数 a,由于存在函数的变量提升,同时会有个局部变量a,括号中 a=2021 给赋值的局部变量,并没有修改到全局变量a,因此打印的结果为 2020。

真的是这样吗??实践是检验真理的唯一标准,我打算亲自试一把~

果不其然,答案是2020,道理真的是上面讲的那样吗?为了验证{}内是否存在函数提升,我们可以在 a=2020前打印 a

var a=1999;	
{	 // area1
 console.log(a);	// 应该打印什么    
 a=2020;
 function a(){
 }
 a=2021;
}
console.log(a);	// 应该打印什么

说干就干,就再试试!

第一个输出的是函数,而不是1999,可以看出函数提升的确实存在的。我眉头一皱,突然觉得问题没有那么简单。为什么2021赋值给了局部变量,而2020却赋值给了全局变量呢?似乎变量提升还不能解释这个现象!经过仔细耐心地分析,似乎line 4的a表示全局变量,而line 7的a就是局部变量。确实是这样吗,line 5原代码函数a的声明,像是把前后划分为两个区域,为了更加明显一点,我们做更明显的尝试:

var a=1999;	
{	 // area1
 console.log(a);	// 应该打印什么    
 a=3000;
 console.log(a);	// 应该打印什么
 a=4000;
 console.log(a);	// 应该打印什么 
 a=5000;
 console.log(a);	// 应该打印什么  
 a=2020;
 console.log(a);	// 应该打印什么      
 function a(){
 }
 a=2021;
 console.log(a);	// 应该打印什么         
 a=2022;   
 console.log(a);	// 应该打印什么         
}
console.log(a);	// 应该打印什么

结果如下:

显然,我们的猜测是正确的,{}区域被function a原来声明的地方,还有函数提升的顶部,被划分为了3个区域。

经过查阅很多资料,可以得知,JavaScript代码引擎采用的是词法JavaScript(ES5前)存在变量提升另外,变量提升还有以下需要注意:

  1. 在js中用var ,function声明的变量都将被提到函数的最顶部。(但是不会初始化
  2. 函数声明的优先级大于变量声明的优先级(function > var
  3. 在函数内部变量提升的优先级会小于函数参数(函数参数 > 函数内部变量提升

词法(lexical)一词指的是,词法作用域根据源代码中声明变量的位置来确定该变量在何处可用。

无论函数在哪里被调用,也无论它如何被调用,它的词法作用域都只由函数被声明时所处的位置决定

与词法作用域相对应得是动态作用域,动态作用域并不关心函数和作用域是如何声明以及在任何处声明的,只关心它们从何处调用。换句话说,作用域链是基于调用栈的,而不是代码中的作用域嵌套。

两种作用域的区别,简而言之,词法作用域是在定义时确定的,而动态作用域是在运行时确定的。

好了有点扯远了,回到最初的题目:

 var a = 1999; { // area1
      console.log(a); // 应该打印什么    
      a = 2020;

      function a() {}
      a = 2021;
    }
    console.log(a); // 应该打印什么

当JavaScript在嵌套环境,可以将调用变量的顺序看为从最小的作用域到最最大的global作用域冒泡获取其值,最先遇到同名则冒泡停止。在line2,打印a,向上冒泡,由于函数声明的提升,命中了函数,因此打印为function。那么line3呢?

执行line3,我们猛然地发现,似乎我们根本不需要a的值,存在冒泡一说吗?虽然原理我还没有想明白,但结果告诉我们不是的。我们姑且做出如下假设:

  1. 对于需要取值的变量,引擎采用的是冒泡模式(考虑变量、函数提升)
  2. 而对于赋值运算这样的操作符左侧变量,并不需要知道其值,那么就与变量提升无关,跟原来声明顺序相关(如 line2 指向全局 a,由于line5声明了局部的a,因此lien6的a指向局部)

俗话说“实践是检验真理的唯一标准”,不如实践一下,在line 3执行 a=a+2020 会发生什么,如果结果是函数+2020,则上述猜想合理。

 var a = 1999; { // area1
      console.log(a); // 应该打印什么    
      a = a+2020;
      console.log(a); // 应该打印什么  
      function a() {}
      a = 2021;
    }
    console.log(a); // 应该打印什么

果然如此,事实证明了前面的猜想是正确的。

但为什么是这样的,该以什么具体的原理和理论去解释呢?

未完待续......

也期待各位朋友展开讨论


感谢文中引用相关参考文章的作者们:

www.cnblogs.com/xiaohuochai…

blog.csdn.net/fiona_lms/a…

www.cnblogs.com/zingp/p/610…

blog.csdn.net/weixin_3808…

若有侵权,联系后删除。


码字不易,转载强著名出处。