在 JavaScript 中,IIFE 最常见的写法是:

(function () {
  console.log('立即执行');
})();

外面那层括号的作用在于让 JavaScript 引擎把 function 关键字解析成函数表达式,避免被当成函数声明。函数声明必须有名字,也不能直接跟调用括号;函数表达式则可以匿名,也可以立即调用。

括号并非唯一选择,任何一元运算符放在 function 关键字前面,都能达到同样效果,例如 !、+、-、~,甚至 void:

!function () {
  console.log('! IIFE');
}();

+function () {
  console.log('+ IIFE');
}();

-function () {
  console.log('- IIFE');
}();

~function () {
  console.log('~ IIFE');
}();

void function () {
  console.log('void IIFE');
}();

为什么一元运算符可以替代外层括号?

关键在于 JavaScript 的语法解析规则:当 function 出现在语句开头时,解析器倾向于把它当作函数声明

function () {
  // SyntaxError: Function statements require a function name
}

一元运算符只能接受表达式作为操作数。解析器看到 !function () {} 它必须把后面的 function () {} 当作一个表达式来处理,否则 ! 就没有合法操作数。于是,function 被强制进入表达式上下文,变成函数表达式。

返回值被改变了

虽然这些写法都能让函数立即执行,但整个表达式的结果并不一样。例如:

var a = !function () {
  return 0;
}();

console.log(a); // true

函数返回 0,!0 得到 true。再比如:

var b = +function () {
  return '42';
}();

console.log(b); // 42

+ 把字符串 '42' 转成了数字。void 则比较特殊:

var c = void function () {
  return 'hello';
}();

console.log(c); // undefined

所以,如果你不关心 IIFE 的返回值,这些写法通常没有问题

分号仍然很重要

使用一元运算符写法时,仍然要注意自动分号插入带来的风险。例如:

var a = 1
!function () {
  console.log('x');
}()

这段代码可能不会按预期解析,因为上一行没有分号,! 可能被当成与上一行相连的一部分。更稳妥的写法是主动加分号:

var a = 1;
!function () {
  console.log('x');
}();

同样,传统的括号写法如果以前一行没有分号,也可能出现类似问题:

var a = 1
(function () {
  console.log('x');
})()

因此,无论选择哪种风格,团队中统一分号习惯都很重要

应该使用哪种写法?

从语义清晰度看,标准括号写法最直观:

(function () {
  // ...
})();

它明确表达了「这是一个函数表达式,并且立即调用」。一元运算符写法更短,在压缩代码或某些老式代码库中很常见,尤其是 !function(){}()。但它也有代价:!、+、-、~ 会改变返回值,void 虽然能明确返回 undefined,但对不熟悉的人来说可能不够直观。

在现代 JavaScript 中,IIFE 的使用场景已经比过去少了很多:块级作用域、模块系统、箭头函数等特性分担了它的一部分职责。但如果确实需要 IIFE,建议根据团队规范选择一种统一风格:追求可读性就用括号;追求简短且不关心返回值,可以用 ! 或 void;无论哪种,都别忘了分号和对返回值的预期。

归根结底,!function(){}() 它和 (function(){})() 一样,都在告诉 JavaScript 解析器:这里是一个函数表达式,请立即调用它