三个 JavaScript 例子中的函数提升为何在Nodejs环境下导致不同的输出?

发布于 1周前 作者 songsunli 来自 nodejs/Nestjs

问题描述

我在学习 JavaScript 的函数提升( hoisting )时,遇到了三个看似相似但输出不同的例子。根据函数提升的原理,这三个例子的输出应该是相同的,越想越觉得奇怪啊

例子 1

var a = 0;
console.log("1 a:", a);
if(true){
    a = 1;
    function a() {}
    a = 5;
    console.log("2 a:", a);
}
console.log("3 a:", a);

经过验证,输出结果为:

1 a: 0
2 a: 5
3 a: 1

例子 2

var a = 0;
console.log("1 a:", a);
if(true){
    a = 1;
    a = 5;
    console.log("2 a:", a);
    function a() {}
}
console.log("3 a:", a);

经过验证,输出结果为:

1 a: 0
2 a: 5
3 a: 5

例子 3

var a = 0;
console.log("1 a:", a);
if(true){
    function a() {}
    a = 1;
    a = 5;
    console.log("2 a:", a);
}
console.log("3 a:", a);

经过验证,输出结果为:

1 a: 0
2 a: 5
3 a: f a() {}

问题

  1. 这几个例子在函数提升方面有何不同?
  2. 为什么每个例子的输出都不相同?

三个 JavaScript 例子中的函数提升为何在Nodejs环境下导致不同的输出?

17 回复
  1. 主要是块和全局作用域在 es5 的非严格模式下混淆了,你每一步解析一下提升过程,注意块作用域内 console 输出一直都是 a 的赋值,但是声明式的 Function a 在提升后对全局作用域 a 的引用关系才是 3a 结果, var / 声明式 Function 的提升优先级
    2. 加个 ‘use strict’ 就一样了
    3. 其实不太需要考虑这个了,直接用 let, const 就好了

建议不学这种糟粕

原因是以前一同学想测试 GPT4 的能力,结果 GPT4 答不上来,我越看也越觉得怪,就上论坛发帖了。stackoverflow 上都说是历史遗留问题,https://stackoverflow.com/questions/58619924/function-declaration-in-block-moving-temporary-value-outside-of-block

不要再研究 sloppy mode 的东西了
我跟你说你这几个 case 在上古浏览器里还有另外不同的表现的

研究屎山是为了消除它,而不是让自己适配它。

对于已经消除的屎山,还拿他做题玩嘛呢?有公司面试题出这个我都直接怼回去,也没耽误我拿 offer

欢迎使用 Safari ,Safari 里三者输出是一样的。在块作用域内定义用 function 定义函数是 JS 中的标准未定义行为,取决于 JS 引擎的实现

例子三怎么解释啊?

吃饱了撑的

你编写的奇怪的代码正在以奇怪的方式运行。

第一行放个 ‘use strict’ 确实能解决不少麻烦。

同一个作用域下不要使用重名的变量,减轻自己的负担。

拒绝使用 const 关键字,我都写 js 了,还 const const const 。

哎,我也是这么认为的,明明有更好的方式来规避这种问题比如 let/const ,可偏偏有公司把这些放到面试题里去。

函数声明在全局也创建了同名变量,声明时改变了全局同名变量

只能这么认为了,就是函数声明提升 var a = function(){},然后紧接着是 window.a = a;然后原来声明之前的 a 赋值,都是 window.a 赋值,函数声明之后的都是 a 赋值。这样能符合所有结果。

boring , i +++++i 等于几?

es6 给了 let,const ,就是用来解决这些问题体操的。

2023 年了 还在纠结这种问题

我之前看一个视频说 function 的提升有时候很奇怪,规范里都没有定,所以这里是引擎按照自己的理解实现的,所以这样的 function 提升在不同浏览器的结果可能都不一样,所以学习这种提升实在是没必要,面试真遇到了也只能可惜的放弃吧

在 JavaScript 中,函数提升(Hoisting)是指变量和函数声明在代码执行前被移动到它们各自作用域的顶部的行为。然而,函数提升的细节和一些特殊情况在不同环境下(如浏览器和 Node.js)可能导致不同的行为,尤其是在涉及到模块系统和作用域时。

以下是一个简单的示例,展示如何在 Node.js 环境下可能遇到的不同输出:

示例 1:全局作用域中的函数提升

console.log(foo()); // 输出: function foo() { ... }
function foo() {
    return 'Hello from foo!';
}

示例 2:模块作用域中的函数提升

在 Node.js 模块中,函数提升的行为略有不同,因为每个模块都有自己的作用域。

// module.js
console.log(foo()); // 输出: ReferenceError: foo is not defined
function foo() {
    return 'Hello from foo!';
}
module.exports = foo;

由于模块的作用域隔离,foo 在声明前无法访问。

示例 3:使用 letconst 声明函数

在 ES6 中,letconst 引入了块级作用域,并且不会提升。

console.log(foo()); // 输出: ReferenceError: foo is not defined
const foo = () => 'Hello from foo!';

在 Node.js 环境下,letconst 声明的函数或变量不会提升,因此尝试在声明前访问它们会导致错误。

总结来说,函数提升的行为可能因作用域(全局、模块、块级)和声明方式(functionletconst)的不同而有所不同。理解这些差异对于编写健壮的 Node.js 应用至关重要。

回到顶部