前一章的內容回顧,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()。