乐趣区

关于前端:编程题为什么最后一个a是1不是5

前段时间有人在知乎上邀请我答复一个问题:为什么最初一个 a 是 1 不是 5?

题目如下:

console.log(a)
if (true) {
  a = 1
  function a() {}
  a = 5
  console.log(a)
}
console.log(a)

我的第一反馈是:undefined,5,5。预计和题主想的一样

剖析一波

假如没有 if(true),即如下代码:

console.log(a)
a = 1
function a() {}
a = 5
console.log(a)
console.log(a)

那么答案什么?

a()、5、5

这解释了两个个性

  1. 变量、函数晋升且函数的权重大于变量
  2. 在 a 没有用 var 申明时,a=XX 默认是用 var 来申明

变量、函数晋升方面的知识点在于:

变量会晋升,函数也会晋升,并且函数晋升的优先级大于变量,如下例:

console.log(a)
console.log(a())
var a = 1
function a() {console.log(2)
}
console.log(a)
var a = 3
a = 4
console.log(a)
console.log(a())

a()、2、1、4、a is not a function

回过头来看这道题目

console.log(a)
if (true) {
  a = 1
  function a() {}
  a = 5
  console.log(a)
}
console.log(a)

if (ture) {},造成了作用域,锁住了这片变量,function a(){} 无奈逃逸。换句话说,只有 {} 块级标识符在,function a() {} 就被所在块级作用域中,也就说在 if (ture) {} 这片块级作用域下,它不会晋升到全局顶层,而是在 if(true){} 下,即代码执行时是这样:

console.log(a)
if (true) {+function a() {};
  a = 1;
  -function a() {};
  a = 5;
  console.log(a);
}
console.log(a)

如果你在 a = 1 前打印 a,a 的值就是 function a(){}

所以这道题全局环境下,没有变量晋升,写在第一行的 console.log(a) 因为找不到 a,所以值为 undefined

进入 if(true) {} 中,function a(){} 函数晋升,且权重最高,所以赋值之前的块级作用域中的 a 为 function a() {}window.aundefined

代码执行到 function a() {} 后,块级作用域中的 a 还是为 1,然而全局变量 a 被赋值为 1

执行到 a = 5,传统赋值,影响的是块级作用域中的 a,而不会影响全局变量 a,所以打印的第二个 console.log(a) 为 5,第三个 console.log(a) 为 1

那么问题来了, 为什么一执行 function a(){},全局变量 a 就被赋值为 1?

我陷入的深思,起初在答复中发现云补断山答复了,说是

历史起因,为了兼容之前的 ES5 的语法,所以在标准规定了块级作用域内函数申明的一些行为,各个浏览器实现可能不一样

简略来说,在块级作用域内的函数函数申明,行为相似于 var,都会在全局作用域申明一个同名变量(也就是 window 上挂一个同名的属性,默认值是 undefined),因为 ES6 遇到块级作用域,会基于块级作用域创立 environment record,寄存以后块级作用域内的变量,所以这个函数申明会晋升到块级作用域顶部(而非全局作用域顶部)

ECMA262 目录 B

咱们学的 JavaScript 是 ECMAScript,然而咱们把代码运行在浏览器上时就要依照浏览器的规范,浏览器里会有一些私货在,最经典的是 __proto__,倒逼 ECMA 驳回。话说回来,依照这位仁兄的意思

// 因为 function a() 申明过,所以全局有个 window.a
console.log(a)
if (true) {
  // 申明归申明,然而函数晋升晋升与作用域相干,所以晋升至此块级作用域顶部
  a = 1
  // 块级作用域中的 a 被赋值为 1
  function a() {}
  // 原地爆炸,执行函数后,全局 window.a 被赋值为块级作用域中的 a
  a = 5
  // 块级作用域中的 a 又被赋值为 5
  console.log(a)
}
console.log(a)

最诡异的是执行 function a() {} 后,全局 window.a 被赋值且为块级作用域中的 a

这个事件没完!!

等等,我就说的玩玩的,如果工作中或面试中真遇到这类问题,我兴许还是不会解。

太诡异了,这不是考题范畴(块级作用域、函数晋升、变量晋升)

就这样先吧

本文参加了 SegmentFault 思否征文「如何“反杀”面试官?」,欢送正在浏览的你也退出。

退出移动版