前一章的內容回顧,a 泡泡包著 b 泡泡。
你在 a 裡面,會看到 b 但是不能拿 b 裡面的東西。
function b(aa){
 var cc = 3;
 console.log( aa + cc );
}
foo( 2 ); // 5
cc; // ReferenceError
Tony 要開車,只需要知道方向盤、油門、煞車、打檔。所以你不會提供 Tony 引擎室裡面的配置。
這是最小權限原則 ( Principle of Least Privilege ) 的軟體設計原理。
或 最小授權 ( Least Authority ) 或 最小曝露 ( Least Exposure )
在軟體設計中,你應該只透露所需要的最少東西,並把其他的東西都「隱藏」起來。
function doSomething(a) {
    b = a + doSomethingElse( a * 2 );
    console.log( b * 3 );
}
function doSomethingElse(a) {
    return a - 1;
}
var b;
doSomething( 2 ); // 15
這裡有兩個併排的泡泡。全域可以取用到 b 和 doSomething,但沒必要。
讓泡泡包著泡泡吧。
function doSomething(a) {
    function doSomethingElse(a) { // 寫在內部。
        return a - 1;
    }
    var b; // 保持私有。
    
    b = a + doSomethingElse( a * 2 );
    console.log( b * 3 );
}
doSomething( 2 ); // 15
這樣開放的只有 doSomething。
產生衝突的例子。
function foo(){                 // step2
    function bar(a) {           // step5 bar(a = 0); step10 bar(a = 4)
        i = 3;                  // step6 i = 3;      step11 i = 3 重複了
        console.log( a + i );   // step7 ( 0 + 3);
    }
    for (var i = 0; i<10; i++){ // step3 i = 0;      step8 i++ = 4;(永遠小於10)
        bar( i * 2);            // step4 bar(0);     step9 bar(4)
    }
}
foo();                          // step1
照著 step 執行後,就會造成 step6~9 重複執行。不斷得到 11 的結果。( 金恐怖 )
要怎麼避免呢?
var i = 3;。j。這邊還沒辦法意會。
(如果載入許多程式庫,這些程式庫如果沒有規範,就會互相使用到全域函數。所以,在製作函式庫之前會先做個命名空間。)
var MyReallyCollLibrary = {
    awesome: "stuff",
    doSomething: function(){
        // ...
    },
    doAnotherThing: function(){
        // ...
    }
};
一樣要避免衝突。但是是用模組的方式達成。
這邊 Tony 等級不夠,等到第五章在看。
如果我們想對原本的變數,切割出範疇。可以用函式。
var a = 2;
console.log(a);
現在要多一個範疇,裡面的 a = 3; 要怎麼做?
加入以下程式碼。
function foo(){
    var a = 3;
    console.log( a ); // 3 
}
foo()
但原本的全域裡面只有 a。增加後的的全域裡面不只 a 還有 foo()。 這樣就「污染」了全域。
而且還要呼叫,才能使用。
就產生了 IIFE (Immediately Invoked Function Expression)立即執行函數。
不汙染 且 馬上執行。
(function foo(){
    var a = 3;
    console.log( a );
})();
如果 function 前面沒東西,就是函式宣告。
如果 function 前面有東西,就是函式運算式。
關鍵差異在於有沒有被繫結 ( bound )。小括號會先結算繫結。
函式運算式有匿名和具名。
比較熟悉的匿名是做為 回呼 ( call back ) 的參數。
setTimeout( function(){
    console.log("I waited 1 second!");
}, 1000);
裡面的 function 沒有名稱,就是匿名。
匿名的缺點
行內函式運算式 ( Inline function expressions )強大且實用沒有明顯壞處。
setTimeout( function timeoutHandler() {
    console.log("I waited 1 second!");
}, 1000);
前面有提到過,這邊再一次列出來。
兩種寫法都可以
(function(){..}()); // 在內執行
(function(){..})(); // 在外執行
var a = 2;
(funciton IIFE( global ){
    var a = 3;
    console.log( a ); // 3
    console.log( global.a ); // 2
})( window );
console.log( a ); // 2
這樣可以在同一個範疇同時使用範疇內和全域的參考。
undefined = true; // 這是假設,別用
(function IIFE( undefined ){
    var a;
    if ( a === undefined ){
        console.log("Undefined is safe here!");
    }
})();
給了 IIFE undefined 的參數,但沒有傳給他任何值。透過判斷,這個範疇下的 undefined 是安全的。(利基微小)
var a = 2;
(function IIFE( def ){
    def( window );
})(function def(global){
    var a = 3;
    console.log( a ); // 3
    console.log( global.a ); // 2
})
def() 作為參數傳入 IIFE(),window 再以參數的形式傳入 def()。