FENote— JS 的IIFE 相關
我認為知識點之間是環環相扣的, 所以用 '相關' 這兩個字是因為我懶極了

前言:
js 作為相較隨意的語言, 有些特殊寫法不去查真的看不懂, 最基礎的地方也常常是我debug最久的地方(笑), 雖然有些寫法怪異了點, 但存在必然是有它的道理, 理解它們的功能也可以對js 特性有更進一步的熟練
作為語言中最重要也是最常用的function, 必須清楚的釐清, 所以這篇作為筆記記錄下函式的相關應用
基本函數的種類
- 函式聲明:
function add(a,b){ return a+b } - 表達式:
var add_it = function add(a,b){ return a+b } - 匿名函式:
function(a,b){ return a+b }
提一下 3.的最常的用法是
var add = function(a,b){return a+b}這個寫法是表達式的一種, 其實只是把add 變數跟一個沒有名字(匿名)的 function 結合而已, 但這個特性讓寫法具有彈性
這邊要提到的是函式聲明不管在哪裡都會被提升到頂端 (hoisting), 這個觀念大概是這樣的, 在寫完程式碼給瀏覽器解讀的時候, 會經過一道給引擎compile的程序, 瀏覽器會將程式碼 ”做” 成它的規格
這裡先不細講這件事, 但有了大概的觀念之後呢, 看看下面的程式碼
add(3,5); //8 it worksfunction add(a,b){
return a + b;
}
如果照寫程式的順序來看, 先調用這個add 應該要是錯的, 但hoisting就幫我們做了這件事
But, 如果是表達式的話, 就會報錯
add(3,5); //errorvar add = function(a,b){
return a+b
}
立即調用函式表達式(IIFE)
function(){ ...})()
//這是我比較習慣用的, 也有另外一種但只是括號不同而已就不列出, 其實用處是相同的
如果有參數的話, 後方括號的參數為傳入給第一個括號的值
(function(name, age){return '我叫'+name+ ', 我' + age +'歲'})('Kris', 666)
IIFE算是匿名函式的應用:
例如:(function(){
alert('HELLO')
})()可以用解釋成兩個步驟1.說哈囉的這個函式;
2.執行它而原本具有名稱的時候var sayHello = function(){
alert('HELLO')
};
sayHello();1. 賦值給sayHello變數
2. 執行sayHello
def: 此函式在定義時立即執行, 且這個函數是匿名的, 將運算過程包起來.
- 因為都是區域變數, 所以可以避免留下全域變數導致混淆, 多人協作的時候尤其明顯
- 函數執行完就啟動垃圾回收機制, 也代表生命週期結束, 省下一些空間
而為什麼會有這種寫法呢 ?
因為要在函式體後加括號作為立即調用的話, 這個函數必須是表達式
如果是函式聲明像這樣立即調用的話,function(){
console.log('HELLO')
}()報錯原因: 編譯時遇上function 關鍵字, 引擎會默認這是函式聲明,而函數聲明是需要名字的所以拋出了語法錯誤除此之外, 這也不是表達式---
那如果加個名字...function hello(){
alert(' Hello World ')
}()又報錯原因: 引擎會解析成//第一部分: 聲明 function hello(){ alert(‘Hello World’); }//第二部分: () 出現錯誤, 會解讀成不相干的表達式---而立即調用更快的方法就是用()將function包起來, 讓引擎解讀為表達式, 再調用(function(){...})()
其實+-~!運算符甚至是 new 都可以作為宣告立即函式且避免報錯的方法, 但有時運算符可能會和返回值進行非預期的運算, 也較難理解語意, 加括號是最安全的做法
以括號來說, 有點像我們平時運算 (a + b) * c 會用小括號先把 a + b 算出來
也算是一種既定的規則了, 因為大家都這麼用嘛 (?)
延伸(看看就好):
也是因為JS中沒有Private的概念, 全域變量可能會被覆蓋, 導致無法正常運作, 所以根據作用域鍊的特性, 使用這種技術可以模擬一個Private, 內部可以訪問外部的變量, 而外部環境不能訪問內部的, 藉此減少變量的衝突, 有時候也稱作 閉包(closure), 有權訪問另一個作用域中變量的函數
閉包:
// 它的运行原理可能并不像你想的那样,因为`i`的值从来没有被锁定。
// 相反的,每个链接,当被点击时(循环已经被很好的执行完毕),因此会弹出所有元素的总数,
// 因为这是 `i` 此时的真实值。
var elems = document.getElementsByTagName('a');
for(var i = 0;i < elems.length; i++ ) {
elems[i].addEventListener('click',function(e){
e.preventDefault();
alert('I am link #' + i)
},false);
}
// 而像下面这样改写,便可以了,因为在IIFE里,`i`值被锁定在了`lockedInIndex`里。
// 在循环结束执行时,尽管`i`值的数值是所有元素的总和,但每一次函数表达式被调用时,
// IIFE 里的 `lockedInIndex` 值都是`i`传给它的值,所以当链接被点击时,正确的值被弹出。
var elems = document.getElementsByTagName('a');
for(var i = 0;i < elems.length;i++) {
(function(lockedInIndex){
elems[i].addEventListener('click',function(e){
e.preventDefault();
alert('I am link #' + lockedInIndex);
},false)
})(i);
}註: 這段應用來自[译] JavaScript:立即执行函数表达式(IIFE)
模塊模式:
var a = (function(){
var name = 'Kris'
var age = 666return{
sayName: function(){
console.log('我叫:' + name)},
howOld: function(){
console.log('我幾歲?' + age)},
yearLater: function(){
age = age +1
console.log('我到底幾歲?' + age)},
}
})();a.sayName(); //我叫:Kris
a.howOld();//我幾歲?666
a.yearLater();//我到底幾歲?667
a.howOld();//我幾歲?667a.name; //undefined
相關連結:
- 閉包closure
- 模塊(Module)模式
- 提升(hoisting)
本文參考資料:
(译)详解javascript立即执行函数表达式(IIFE)
[译] JavaScript:立即执行函数表达式(IIFE)
兩篇好像都是翻譯自 原文:Immediately-Invoked Function Expression (IIFE)