美团前端二面,读代码题求解

var a = 0;
if (true) {
    a = 1;
    function a() {}
    a = 21;
    console.log(a);
}
console.log(a);

求两次 console 出来的值。

答案是21 1。

本菜鸡想了很久也没想明白第二个为啥是1。。

求大佬解惑 ?

阅读 4.6k
6 个回答

21 1 的输出结果不完全正确,在不同的浏览器下结果不同
在safari下结果为21 21
在chrome下结果为21 1

同时ECMAScript规范中说函数声明可以嵌套在条件语句块内,但是其运行的结果依赖于JS解析器的实现,其结果具有不确定性,不推荐在生产环境下使用

Functions can be conditionally declared, that is, a function statement can be nested within an if statement, however the results are inconsistent across implementations and therefore this pattern should not be used in production code. For conditional function creation, use function expressions instead.

面试官告诉你为什么了吗?
老实说我怀疑这是个未定义行为,反正我是没找到标准中关于这种情况的说明。

根据结果来分析一下,我觉得根源在于js没有为闭包准备一个关键字。
代码简化一下变成这样:

{
    a = 1;
    function a() {}
    a = 21;
    console.log(a);
}

代码块中有方法定义就会自动产生闭包, 因为有{}的存在它又最多只会提升到这段代码块的最上面, a就变成了这段代码块中的局部变量。

a = 1 给"全局"变量 a 赋值
在方法定义之后的 a=21 其实是给局部变量赋值。

如果代码是这个样子我相信任何人都不会觉得难理解:

var a = 0;
{
  a = 1;
  var a_ = _closure() {};
  a_ = 21;
}

附 v8 的bytecode, node版本:v14.7.0

function test() {
  var a = 0;
  {
    a = 1;
    function a() {}
    a = 21;
    // console.log(a);
  }
  // console.log(a);
}
test();
$ node --print-bytecode --print-bytecode-filter=test test.js
[generated bytecode for function: test (0x02368a073831 <SharedFunctionInfo test>)]
Parameter count 1
Register count 2
Frame size 16
   29 S> 000002368A0743FE @    0 : 0b                LdaZero
         000002368A0743FF @    1 : 26 fb             Star r0
   29 E> 000002368A074401 @    3 : 81 00 00 02       CreateClosure [0], [0], #2
         000002368A074405 @    7 : 26 fa             Star r1
   42 S> 000002368A074407 @    9 : 0c 01             LdaSmi [1]
         000002368A074409 @   11 : 26 fa             Star r1
   69 S> 000002368A07440B @   13 : 26 fb             Star r0
   75 S> 000002368A07440D @   15 : 0c 15             LdaSmi [21]
         000002368A07440F @   17 : 26 fa             Star r1
         000002368A074411 @   19 : 0d                LdaUndefined
  135 S> 000002368A074412 @   20 : aa                Return
Constant pool (size = 1)
Handler Table (size = 0)
Source Position Table (size = 15)

ECMAScript5标准里并没有描述“代码块里的函数声明”的行为,具体怎么执行要看引擎行为了,就上面的代码在Chrome和IE里执行结果就不同:

// IE
21
function a() {}

JSLint发现“语句块里声明函数”也会报错的,可以看看解释"Function declarations should not be placed in blocks"

~出这样的面试题说明面试官比较虚

搜了一下块内函数声明,貌似这个可能是答案

var a = 0       // 这里定义一个全局作用域变量a 并初始化为 0
if (true) {     // 一个简单的if语句,制造一个简单的局部/词法作用域
    a = 1    // js的奇怪设定 没有发现局部变量  那么修改的就是全局作用域的a
    function a() { }  // 声明一个局部变量a 此局部变量a在这一行是一个空函数
    a = 21  // 受上一句影响,js以为 你修改的是新声明的 局部变量a
    console.log(a)    // 打印局部作用域的a (毕竟你已经声明了一个局部变量a)
} // 局部/词法作用域结束
console.log(a)// 打印全局作用域的a

如此解释,可?
image.png

我也同意微软的观点,面试官这样难为人真的好吗

image.png

非答案,贴个同样坑的代码

var c = [{a:0}];
console.log(c);

c[0].a = 1;
console.log(c)
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏