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

简介: 前段时间有人在知乎上邀请我回答一个问题:为什么最后一个 a 是 1 不是 5?[1]

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


题目如下:


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


image.png


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


分析一波



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


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


那么答案什么?


a()、5、5


这解释了两个特性


  1. 变量、函数提升且函数的权重大于变量


  1. 在 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?


我陷入的沉思,后来在回答中发现云补断山[2]回答了,说是


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


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


ECMA262 目录 B[3]


我们学的 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


这个事情没完!!


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


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


就这样先吧


参考资料



[1] 为什么最后一个 a 是 1 不是 5?: https://www.zhihu.com/question/492311084


[2] 云补断山: https://www.zhihu.com/people/xie-guo-feng-46


[3] ECMA262 目录 B: https://tc39.es/ecma262/#sec-additional-ecmascript-features-for-web-browsers

相关文章
|
数据可视化 数据库
|
算法 C++
2023_7_21编程题
2023_7_21编程题
65 0
7-188 编程团体赛
7-188 编程团体赛
202 0
|
程序员
编程会是你自由职业的一种选择吗
欢迎来到我的小院,前几天有个曾经我带过小弟问我,大侠我最近想从事自由职业,你能帮我参谋参谋嘛,我很高兴他来咨询我。思考后,我写了这段建议,希望可以帮助到他,同时也能给想从事自由职业的年轻人一些参考。
编程会是你自由职业的一种选择吗
|
Web App开发 移动开发 前端开发
Day 26: TogetherJS —— 让我们一起来编程!
今天的《30天学习30种新技术》挑战,我打算学习一个源自Mozilla的很酷的JavaScript库——TogetherJS。几个月前,我写过一个面向Java 8的在线Java编辑器。今天我将学习如何使用TogetherJS来给这个应用增加协作功能。
205 0
Day 26: TogetherJS —— 让我们一起来编程!
|
机器学习/深度学习 人工智能 自然语言处理
编程需要了解的问题
  儿童编程则是一门有趣的课程 ,与成人编程相比学习编程有着明确的目的,或者是为了加薪、跳槽。接下来给大家讲讲家长要了解的编程问题,希望对你们有帮助。许多重视教育的家长都意识到,让孩子从小就开始编程,培养编程思维是一种顺应时代的选择。当孩子在学习编程时,作为父母,有五个方面必须明确。1.什么是steam教育儿童编程,从简单的角度来说就是教儿童学习编程。有些家长不明白,编程这种大学计算机专业才会涉及的专业知识,为什么孩子也要学呢?情况就是这样的,随着人工智能的发展,我们孩子未来生活的世界将是一个用代码编写的高科技智能世界。想象一下,如果孩子那时候还没有编程思维,那么就很难融入那样一个社会。儿童编
152 0
|
存储
编程
编程
220 0
下一篇
DataWorks