zl程序教程

您现在的位置是:首页 >  Javascript

当前栏目

从一道诡异的JS面试题说“作用域”与“提升”

2023-03-09 22:10:33 时间

 “面试造火箭,工作拧螺丝”。面试题目之诡异,常令人匪夷所思。试看一道考察“Hoisting"的面试题目:

一、提升全局变量 var

  1. var tmp = new Date(); 
  2.  
  3. function f() { 
  4.     console.log(tmp); 
  5.     if (false) { 
  6.         var tmp = "hello"
  7.     } 
  8. f(); 

JS新手往往会以为将正常打印出日期,而实际输出的确是`undefined`!

  1. > var tmp = new Date(); 
  2. function f() { 
  3. ...     console.log(tmp); 
  4. ...     if (false) { 
  5. .....         var tmp = "hello"
  6. .....     } 
  7. ... } 
  8. > f(); 
  9. undefined  

这是因为在函数f()的内部,var被提升到定义域的顶部,实际执行为:

  1. var tmp = new Date(); 
  2.  
  3. function f() { 
  4.     var tmp;// 提升到这里,将全局的tmp覆盖了。var默认赋值为undefined 
  5.     console.log(tmp); 
  6.     if (false) { 
  7.         var tmp = "hello"
  8.     } 
  9. f(); 

也就是说var不仅提升,而且将tmp初始化赋值为undefined。

二、如何才能正常输入日期呢?

解决方案是将global-scope的var替换为block-scope的let:

  1. var tmp = new Date(); 
  2.  
  3. function f() { 
  4.     //var tmp;// 提升到这里,将全局的tmp覆盖了。var默认赋值为undefined 
  5.     console.log(tmp); 
  6.     if (false) { 
  7.         let tmp = "hello"
  8.     } 
  9. f(); 
  10. // 2021-04-02T10:52:30.983Z 

这是因为let定义的是local-variable.

三、TDZ临时DeadZones

更加诡异的案例,来单独看let:

  1. var tmp = new Date(); 
  2.  
  3. function f() { 
  4.     console.log(tmp); 
  5.     let tmp = "hello"
  6.  
  7. f(); 

你原以为将会如常打印出时间,但却报错tmp未定义。

  1. ReferenceError: Cannot access 'tmp' before initialization 

这是因为 tmp 被提升,其实际执行为:

  1. var tmp = new Date(); 
  2.  
  3. function f() { 
  4.     let tmp; // 提升在这里 
  5.     console.log(tmp); 
  6.     let tmp = "hello"
  7.  
  8. f(); 

然而区别于var的是,tmp仅仅被提升,却不会被自动赋值为undefined,因此会报错`ReferenceError`.

该问题就是传说中的TDZ (temporal dead zone)。解决方案也简单,就是将所有的let或者const等全部都写到最上面。