JavaScript中的IIFE(立即调用的函数表达式)仍然相关吗?

4

我的理解是,iife可以让你模拟一个"私有"作用域,这样就可以防止全局作用域变得混乱。

现在,由于ECMAScript 6已经被广泛使用,并且通过const和let提供了块级作用域访问,是否还有其他原因使用iife模式呢?(除了需要向后兼容...)


这主要是一个基于个人观点的问题,但我认为立即调用的异步和生成器函数在未来仍然很重要。 - MinusFour
函数的一个优点是能够使用 return 来提前停止执行。您还可以使用参数和参数别名周围的词法变量。函数还可以为调试命名程序。最后,您可以从内部递归/重新启动命名的 IIFE,而没有对块的引用。 - dandavis
@dandavis - 所有这些好处也可以通过使用命名函数来实现,这是大多数语言处理此问题的方式。也许有时内联访问父级作用域变量会很有用。 - jfriend00
1
大多数使用IIFE的情况已经消失了,因为块作用域变量解决了IIFE最常用的问题。尽管如此,仍然有一些不太常见的原因偶尔需要使用它(虽然我在我的代码中还没有遇到过这样的情况)。 - jfriend00
2个回答

4
IIFE的一个优点是可以将其输出赋值给const,这是普通块无法实现的。例如:

const output = (() => {
  // some calculations that take a few lines
  const now = new Date();
  return now.getMinutes() + ':' + now.getSeconds();
})();
console.log(output);

假设我们想要将now变量封装在IIFE中,因为它仅与计算output相关,而在其他地方不需要。如果使用普通块来实现这个目的,只有在使用let而不是const声明output时才可能实现:

let output;
{
  // some calculations that take a few lines
  const now = new Date();
  output = now.getMinutes() + ':' + now.getSeconds();
}
console.log(output);

因为 const 明显提高了代码的可读性(在我看来),这可能是偏爱 IIFE 而不是普通块的潜在原因,当然,代码风格是有所不同的。
话虽如此,如果你想要 无限制地提取,一些人可能会建议避免使用 IIFE,而是声明一个独立的函数,然后导入并调用它:

function getTimeStr() {
  const now = new Date();
  return now.getMinutes() + ':' + now.getSeconds();
}

// another file:
// import getTimeStr from './getTimeStr';
const output = getTimeStr();
console.log(output);

正如上面的代码片段所示,使用像Webpack这样的现代代码捆绑器(或者可能是ES6模块)可能会对使用IIFE的次数产生更大的影响,而不是ES6使用普通块的能力。这取决于个人喜好的编码风格,但使用IIFE肯定是一个可能选择的选项。

1
我不理解你的第一点。const now = new Date(); const output = now.getMinutes() + ':' + now.getSeconds();。当然,now在其他地方没有用到,但谁在乎呢?它是const类型,无论如何都不会被更改。这似乎不是使用IIFE的常见或有用的理由。为了隐藏一个临时变量而使代码复杂化,这并不合适。 - jfriend00
这只是一个例子。对于只有一个变量和两行代码的情况,看起来确实很傻,但如果需要进行更复杂的计算,并且需要更多的中间变量才能得出最终的“output”,将所有的计算都封装在一个函数中(可能是IIFE)会更有意义。特别是当包含整个代码的函数/块很长时(当然,如果可能的话,最好避免使用长函数)。作用域中的变量越少,认知负荷就会(稍微)减少。 - CertainPerformance
重点只是这是一个可能的选项,有人可能会选择它。不过,在大多数情况下,我更喜欢提取为一个命名函数。 - CertainPerformance
1
你展示的例子迫切需要制作成可重用的命名函数。可能会有更复杂的例子,涉及许多中间变量和访问父级作用域变量的情况,这些情况不太容易制作成命名函数。但是,在那种情况下,代码可能只需要以其他方式简化,而立即调用函数表达式(IIFE)也可能不是正确的解决方案。 - jfriend00
如果您正在使用IIFE创建一个保持不变的值,为什么不直接创建一个闭包呢? - Phil

1
几乎我在代码中使用IIFE的所有常见原因都已被块级作用域变量替换。
我在真实代码中看到的唯一用途是模拟顶层的await
(async function() {
    try {
        const data1 = await something1();
        const data2 = await something2(data1);
        ...
     } catch(e) {
        ...
     }
})();

我认为使用IIFE的剩余原因是当你想要函数的优点,比如使用returnthrow来停止剩余代码的执行(而不是嵌套在一个if中),或者当你想要递归调用某些东西时。但是,当然,你也可以通过本地作用域命名函数获得所有这些优点。
但是,如果您的函数中的代码从访问父级作用域变量中受益匪浅,那么它真正希望被内联,并且这就创造了一个可能的理由,即使用IIFE将独立函数的优点与内联访问父级作用域相结合。
现实情况是,大多数/所有这些情况都可以通过稍微重构来解决,以在本地作用域中定义命名函数(因此仍然可以访问父级作用域变量),或者只需将事物从父级作用域传递给命名函数,这样它就可以以这种方式访问它们,事实上,在其他语言中,我们都这样做。唯一的代价是多一个本地函数名称(如果您想隐藏它,甚至可以是本地作用域)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接