node.js (Windows) 对 IIFE 的奇怪观察

strange observation on IIFE in node.js (Windows)

nodejs 是否会出现以下行为?在我看来它有问题。如果不是,我错过了什么?

var abc = function(){
console.log("hello");
}

(function(){
  console.log("welcome");
})();

我得到以下异常

TypeError: undefined is not a function
    at Object.<anonymous> (C:\node\main.js:8:3)
    at Module._compile (module.js:460:26)
    at Object.Module._extensions..js (module.js:478:10)
    at Module.load (module.js:355:32)
    at Function.Module._load (module.js:310:12)
    at Function.Module.runMain (module.js:501:10)
    at startup (node.js:129:16)
    at node.js:814:3

如果我将代码更改为

var abc = function(){
console.log("hello");
}

(function(){
  console.log("welcome");
}());

它产生

welcome hello

我不得不相信节点解析错误地假定 嵌套 IIFE (function(){...})()) 首先执行并且 IIFE 触发器的外部 ()在它上面执行函数定义(如果我在 abc 定义和 IIFE 之间引入注释行也没关系)。

导致您出现问题的原因是缺少分号。

var abc = function(){
console.log("hello");
}; // missing semicolon!

(function(){
  console.log("welcome");
})();

这不仅仅是一个 node.js 问题。您将在浏览器中遇到相同的错误。正如其他人所提到的,错误是第一个函数中缺少分号。但是这是怎么回事?

好吧,首先让我们解释一下到底什么是IIFE。您可能知道以下语法:

(function(){})();

但这不是 IIFE 的唯一语法。 IIFE 即立即调用函数表达式。所以它是在声明后立即调用一个函数表达式。

那么,什么是函数表达式?它只是一个在表达式上下文中声明的函数。在表达式上下文中求值的一种方法是使用大括号运算符:()。在这里,大括号运算符实际上与数学中的大括号运算符相同:它强制执行数学运算的优先级。

所以这里的大括号:

(function(){})

意思和这个完全一样:

(1+1)

它告诉解释器,它里面的代码是一个表达式,return是一个值。这就是表达式上下文的意思 - 任何地方都可以对 return 值进行计算。

语言在其他地方解释为表达式上下文。其中之一紧跟在操作员之后。例如 ! 运算符或 - 运算符(使数字为负数)。所以,例如你可以这样写:

-12 + 5

你也可以这样写一个IIFE:

-function(){}()

javascript 解释为表达式上下文的另一个地方是 = 符号右侧的所有内容。例如:

var x = 12 + 5;

也就是说,你可以这样写一个 IIFE:

var x = function(){}();

这就是导致您的代码出现问题的原因。基本上,javascript 会像这样解释您的代码:

var abc = function(){
    console.log("hello");
}(function(){console.log("welcome")})();

也就是说,您的第一个函数被视为 IIFE,通过将第二个函数作为参数传递来调用它,并尝试调用第一个函数的 return 值(这是未定义的,因为您没有return 除了记录“你好”之外什么都没有。

换句话说,如果我们分解它,它就是这样做的:

function first_function () { console.log('hello') }

function second_function () { console.log('world') }

var temp = first_function(second_function);
var abc = temp(); // it errors here because temp is undefined

教训是,函数声明后不需要分号,但函数表达式后需要分号。

第二,不太有用的教训:IIFE 的方法不止一种。