js全局变量和同名函数的问题

{
  function foo() {
    console.log(1)
  }
  foo = 1;
  function foo() {
    console.log(2)
  }
  foo = 2;
}

console.log(foo) // 1
console.log(window) // window中有属性foo:1

各位老师们,为什么这里输出foo是1啊?能不能帮小弟解释一下。

阅读 4.6k
5 个回答

问就是未定义行为,不同的环境得到的结果不一样,所以也不要从语法层面去深究,会翻车的。
你这个例子还好,至少

  • 最新FF最新Chrome 还有 IE10- 的表现几乎一致;
  • Chrome50 表示有语法错误,得改成if(true),改了之后跟上面一致;
  • IE11则得到第二个函数(这是唯一一个我觉着能解释通的现象:先处理变量,后处理函数)。

加个子函数,这个例子就变得更生猛了:

{
  foo = console.log('正在置1,此时 foo=', foo) || 1;
  function foo() {
    console.log(1)
  }
  function foo() {
    console.log(2)
  }
  foo = console.log('正在置2,此时 foo=', foo) || 2;
    console.log('区域打印,此时 foo=', foo);

    (function(){
        console.log('子函数打印,此时 foo=', foo);
    })();
}
console.log('全局打印,此时 foo=', foo);

结果就是:

  • Chrome94 还很嘴硬地说是 1
  • FF94直接说不知道(undefined);

    如果把外面的花括号换成 IIFE,那么试图在全局访问foo会报Uncaught ReferenceError——也就是说,使用括号而非IIFE的时候,FF肯定了foo这个全局变量的存在,但又真的不知道是什么值(或者去子函数浪荡一圈之后忘了还有个全局变量?)
我本来还编了一段解释,但这个例子一出来,任何从语法出发、忽视运行环境的解释都没法圆全场。

如果哪个面试官拿这个考你,你得认真给他掰扯掰扯,说不定你还能获得成就“把面试官比下去了而被刷”。

突然有一个想法:就是用 babel 把这样一段代码转成 AST ,看看社区对这个问题怎么理解的,不过我是懒(bu)得(hui)去做了。

我不建议去研究这种意义不明确,或者不直观的语句。
应该在写代码的时候尽量避免意义不明确/不直观的代码。

不管是自然语言还是计算机语言,在用于沟通的时候,都需要意义尽可能的准确。

在浏览器里调试一下可以发现问题所在,在ES5里没有块级作用域的情况下,第一行 function foo的时候,foo 就被挂载当成 window 的全局属性了,这时的 foo 是一个函数的指针,执行到 foo = 1 的时候,挂载在 window 上的 foo 不再以指针引用的形式存在了,变成了原始值 1。这样后续 foo 的变化也不会影响 window 上挂载的 foo 的值了。我猜大概是这样的原理。

应该是 {} 代码块内部变量提升导致的诡异问题,

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题