中英对照

// IIFE, Immediately-invoked Function Expressions 立即调用函数(表达式)
// FD, function declaration, 函数声明
// FE, function expression, 函数表达式

立即调用需要把函数声明变成函数表达式

// function 开头的代码被视为函数声明

// 函数声明需要名称
function(){console.log(1)}()
		// Uncaught SyntaxError: Function statements require a function name
		// 可以看到根本就没有解析到后面的() 就嘤了
// 分组符号内不能为空
function name(){console.log(1)}()
		// Uncaught SyntaxError: Unexpected token ')'
		// 加上名称终于解析到后面的() 了, 但是空的分组被视作语法错误

// JavaScript 不支持声明函数后立即执行
function name(){console.log(1)}(0) // 终于没有错误了, 但是函数体并没有被执行
// 这相当于
function name(){console.log(1)}; // 声明一个函数
0; // 无意义的代码

// 所以立即执行的关键就在于, 代码不以function 开头 😛

分组符号() 内的代码就被视为表达式

1 // (1)
(1) // (表达式)
(function(){}()) // 此时函数不是函数声明, 而是函数表达式, 所以可以立即调用
(()=> {})() // arrow function 版本

// 这两种为什么都行, 看花眼了😂
( function(){}() )
( function(){}) ()
// 我理解为上面的都是缩写, 完整版:
( ( function(){}) () )

// (◎﹏◎) 😵 🙈

参与运算就被视为表达式

// 一元运算符(只需要一个参数)
+function(){console.log(1)}()
-function(){}()
!function(){}() // ! 对boolean 值取反
~function(){}() // ~ 对底层二进制数挨个取反
++function(){}() // 会对函数的默认返回值undefined 自增, 然后报错
--function(){}() // 但是函数已经执行完毕
delete function(){}()
typeof function(){}()
void function(){}()

// 二元运算符
1 + function(){}()
1 & function(){}()
const waste = function(){}() // 赋值也算运算吧(小声)

// 三元运算符
true ? function(){}() : ""
NaN ? NaN : function(){}()
1 ? function(){)}() : 1

// 还是(function(){})() 好, 没有运算

逗号操作符

// 住手, 停下来啊, 还要去找工作啊

// 逗号操作符可以在一个表达式的位置塞入多个表达式(常用在for 循环中提供多个参数)
1 // 一个位置
(1) // 一个位置
(1,2) // 放入了两个表达式, 返回最后一个表达式的值, 但是两个表达式都会运行
1,2 // 省略括号, 即分组符

1, function(){}()
true, function(){}()
"", function(){}()

// 但是要铭记规则, 不能以function 开头
function(){console.log(111)}(), function(){console.log(222); return 333}()
	// Uncaught SyntaxError: Function statements require a function name

// 两个函数都被运行, 并返回最后一个函数的值
0, function(){console.log(111)}(), function(){console.log(222); return 333}()
// 111
// 222
// 333

带名称也行, 甚至可以递🐢

// 只要是表达式, 带名称也没问题
+function name(){}()
"", function name(){}()
(function name(){})()

// 名称无法在外部访问, 这也是立即调用函数的主要目的 - 局部作用域
"", function name1(){}()
name1 // undefined

// 名称在内部可以用, 可以用来递归
(function name(i) {
  if (i && i < 10) console.log(i++); // 稍有不慎, 递不归 - 鲁树人
  else return i;
  name(i);
})(1);