JS在{}中插入了一个与变量同名的函数,输出的结果却不是最后的赋值,为什么?

    var a = 1;
    if (true) {
        console.log(a)//函数 函数提升
        a = 2;
        console.log(a)//2
        function a() { }
        a = 3
        console.log(a)//3
    }
    console.log(a)//2 为什么???
阅读 3.3k
5 个回答

conditionally created functions

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.

由于历史的原因,块级函数定义的实现是非常混乱的。不同的环境的结果会很不同。至于为什么会出现某一个结果,也只能去咨询每一个具体环境(不过好像没啥地方咨询),或者看源码(如果有的话)。

这种东西建议别研究了 脑子都研究坏了 没啥意义
工作中这么写代码si都给你打出来
知道基本的变量提升规则,块级作用域就行了

a = 3 // 这里是对if中的块级作用域的a赋值,所以不会影响外部的a

正如fefe所说,在非严格模式下,结果是不可预测的。不同的浏览器和引擎对如何处理块中的函数声明实现了自己的规则。

为什么这个第四条 log 打印 2 呢
因为,根据规范函数声明是在当进入上下文时填入的; 同一周期,在进入上下文的时候还有一个变量声明“a”,变量声明在顺序上跟在函数声明和形式参数声明之后,而且在这个进入上下文阶段,变量声明不会干扰上下文中已经存在的同名函数声明或形式参数声明

上下文填充的顺序是: 函数的形参 -> 函数申明 -> 变量申明。

结论就是,你声明的 function a(){} 阻挡了下面的 a 赋值, 不会让 funciton 下面的赋值影响到最外层 a 的值
是因为你在 if 的作用域内声明了 函数

函数声明的实际规则如下:

函数声明只能出现在程序或函数体内。从句法上讲,它们 不能出现在Block(块)({ ... })中,例如不能出现在 if、while 或 for 语句中。因为 > Block(块) 中只能包含Statement语句, 而不能包含函数声明这样的源元素。另一方面,仔细看一看规则也会发现,唯一可能让表达式出现在Block> (块)中情形,就是让它作为表达式语句的一部分。但是,规范明确规定了表达式语句不能以关键字function开头。而这实际上就是说,函数表达> 式同样也不能出现在Statement语句或Block(块)中(因为Block(块)就是由Statement语句构成的)。

但这样写会有疑惑, 所以就有了块级作用域, 用人为标定的方式来划定变量的生效范围, 从而解决编写时的困惑, 就 let 和 const
如果

    let a = 1;
    if (true) {
        console.log(a)//函数 函数提升
        a = 2;
        console.log(a)//2
        function a() { }
        a = 3
        console.log(a)//3
    }
    console.log(a)//1  就变成了 1 , 那就是用 let 人为的划分了这个 a 的生效范围就在最外层声明的范围内 , 不会影响到 if 的块级作用域 , if 的块级作用域也不会影响到外面, 所以以后推荐用 let 来声明变量

看你问问题的时间也是搞到了好晚,在新手阶段,不要太深究这个细节,等你能静下心去看看红宝书 <<你真的懂 javascript? >>的这些原理书籍
这些自然就懂了, 这些在工作中遇不到的问题 如果一直深究,就会让你走近时间的深渊,无法自拔, 工作也做不好, 原理还没搞懂.

上面的总结也是从大叔的博客总结的 ,
如果还想深入了解,就看看这位
汤姆大叔的博客https://ock.cn/gkap7

“函数可以被有条件来声明,这意味着,函数声明可能出现在一个 if 语句里,但是,这种声明方式在不同的浏览器里可能有不同的效果。因此,不应该在生产环境代码中使用这种声明方式,应该使用函数表达式来代替。”——MDN

        var a = 1;
        if (true) {
            console.log(a)//1
            a = 2;
            console.log(a)//2
            a = function () { }
            console.log(a)//f(){}
            a = 3
        }
        console.log(a);//3

结论是函数提升会导致块语句下的同名赋值无法映射到全局变量,可以使用表达式来替代

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