隨手扎
什麼是IIFE(Immediately Invoked Function Expression)
前言
這原本我是在Facebok一篇貼文的回應,因為覺得還蠻清楚的,所以修改過來這裡紀錄一下。(當然也可能我有理解錯誤就是)
什麼是IIFE
根據MDN是這樣寫的:
It is a “design pattern” which is also known as a Self-Executing Anonymous Function
(他又稱為 Self-Executing Anonymous Function,也是一種常見的"設計模式”)
注意 設計模式(design pattern)
這不是特指某一種技術、特性。而是思想、設計。竟然是想法,實現有差異、認知有差異可以理解的。
所以按照中文定義( 定義完馬上就執行 ),淺層廣義的來看:
function f(){};
f();
確實也是定義後執行
撰寫風格上OK,只是益處不大。
(f = function(){})() // execute first
f() // execute second
也是一樣。(這再js是合法的,之後還可以在執行f()
)
但就語法上就更精簡許多
從狹義、技術來看,是看是不是有附值(函式命名也是),然後才執行。 如此剛題的兩個,都是命名後才執行,狹義不算是IIFE 不過這終究只是一種設計模式,實現方式沒有一定。
function f(){};
f();
如果上面的形式,編譯器可以做優化,而直接忽略f
,那他也可能是狹義的定義。
(另外這裡廣義、狹義只是我片面的解讀)
此外,在維基百科這麼寫到:
“立即呼叫函式表達式” 最早稱為「自執行(匿名)函式」 但是立即執行的函式不一定是匿名的。 ECMAScript 5的 strict mode 禁止arguments.callee 因此,這個術語不夠準確。
這或許也側面證明我的想法。
從組語來看函式可能只是一個label。但是動態語言,更有可能是開闢一個空間,然後把code複製過來。這個空間可能直接在stack裡,也可能在heap裡。而命名函式相當於在索引的table,註冊一個key,指向這個位置。正常宣告函數時,可能也隱含這個行為。
另外程式語言在解析時,通常經過下面幾個階段:
parse -> AST -> IR -> exec
ES6的let
可能在AST或是IR階段就被發現,在宣告前就使用,才有TDZ,可以提前報錯(不過我不清楚node.js實際怎麼做的)。宣告和定義在js是兩回事。
- 宣告->開闢記憶體空間
- 定義->賦予值
IIFE的好處
上面說到,函式可能存在heap或是stack空間,要是不去檢索(函式名稱),直接跳轉到該記憶體位置執行,就可以加速執行。(有點像尾遞迴提前展開堆疊)
原本想畫圖說明,可現在懶的畫了wwwww
EMCASCript的一些行為嘗試
function run1(){
console.log("-------run1------");
(() => {
console.log(`a = ${a}, b = ${b}`);
})();
var a = 10;
var b = 50;
}
function run2(){
console.log("-------run2-------");
(f = () => {
console.log(`a = ${a}, b = ${b}`);
})();
var a = 10;
var b = 50;
}
function run3(){
console.log("-------run3-------");
var a = 10;
var b = 50;
(() => {
console.log(`a = ${a}, b = ${b}`);
})();
}
function run4(){
console.log("-------run4-------");
var a = 10;
var b = 50;
(f = () => {
console.log(`a = ${a}, b = ${b}`);
})();
}
run1();
run2();
run3();
run4();
Output:
-------run1------
a = undefined, b = undefined
-------run2-------
a = undefined, b = undefined
-------run3-------
a = 10, b = 50
-------run4-------
a = 10, b = 50
Python
(lambda:print("Hello"))()
如果是Python 3.8後,就可以:
(f := lambda:print("Hello"))()
f()
C++
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
int main()
{
[](){cout << "Hello, World123123." << endl;}();
auto f = [](){cout << "Hello, World." << endl;};
f();
void (*f2)();
(f2 = [](){cout << "babababa" << endl;})();
f2();
return 0;
}
/*
Output << EOF
Hello, World123123.
Hello, World.
babababa
babababa
<< EOF
*/
關於AST
可以參考這篇。甚至可以直接嘗試看看:https://esprima.org/demo/parse.html 。