iT邦幫忙

2022 iThome 鐵人賽

DAY 11
0
Modern Web

就是要搞懂 JavaScript 啦!系列 第 11

Day11 咱們井水不犯河水──作用域的優點

  • 分享至 

  • xImage
  •  

說了這麼多,到底為什麼要使用作用域呢?

說到底,列出一系列對變數存取的規則,目的就是要製造出一個個作用域泡泡,只有在合乎規定的情況下,才能夠使用泡泡內部的變數,在泡泡外的世界,內部一切東西都是隱藏的。

這麼做的好處包括:

  • 維持「最小權限原則」,避免變數或函式被不當存取(比方說由非正規管道修改了函式內部的變數,讓最後計算結果不如預期)
  • 避免同名識別字衝突,導致值被意外覆蓋
    • 避免汙染全域變數
    • 方便模組管理

最小權限原則(principle of least privilege):又稱為「最小授權(least authority)」、「最小暴露(least exposure)」,意指程式執行時只允許訪問當下必要的信息或資源,避免受到錯誤或者惡意行為的破壞。

以下進行更詳細的解釋:

最小權限原則

function calculate(a) {
	b = a + calculateMore( a * 2 );

	console.log( b * 3 );
}

function calculateMore(a) {
	return a - 1;
}

var b;

calculate( 2 ); // 15

在上面這段程式碼中, bcalculateMore 都只在 calculate 內部使用,卻暴露在全域變數中,有可能會被意外更改或覆蓋。更好的做法是把它們都藏入 calculate 內部,僅提供 calculate 的函式作用域存取:

function calculate(a) {
	function calculateMore(a) {
		return a - 1;
	}

	var b;

	b = a + calculateMore( a * 2 );

	console.log( b * 3 );
}

calculate( 2 ); // 15

如此一來,bcalculateMore 從外部都無法存取,在保有同樣功能的條件下,卻更不容易出錯。

因此彼此將私有資源暴露在外,將這些細節隱藏起來被認為是更好的程式寫法。


避免同名識別字衝突

function foo() {
	function bar(a) {
		i = 3; // 改變外層作用域的 i
		console.log( a + i );
	}

	for (var i=0; i<10; i++) {
		bar( i * 2 ); // 因為 i 永遠是 3 所以陷入無限循環
	}
}

foo();

以上範例中,在 for 循環執行 bar()時,由於 bar() 內部改變了屬於外層作用域變數 i 的值,i 的值永遠停留在 3 無法遞增,因此造成無限迴圈。

全域命名空間(Global Namespace)

命名空間(namespace)基本上可以理解為擁有名稱的作用域,或者說,命名作用域以便存取指定作用域內部的某個變數。
一般的作用域除了 global 以外通常難以獨立調用,而將作用域命名,便可靈活地存取這些作用域。

一般的函式庫基本上都會隱藏自己內部使用的變數和函式,以避免與全域作用域中的變數產生衝突。

在 JS 中,命名空間通常以物件形式存在,函式庫的功能則都作為屬性掛在這個物件底下,避免直接暴露在全域作用域,對其他引用的函式庫彼此影響。

以下是一個作為命名空間的物件範例:

var MyReallyCoolLibrary = {
	awesome: "stuff",
	doSomething: function() {
		// ...
	},
	doAnotherThing: function() {
		// ...
	}
};

模組管理(Module Management)

另外一種避免汙染全域作用域的方式則是使用模組(Module),利用模組管理器將各個函式庫作為模組導入程式。

這些管理器會要求明確地引入某個函式庫的識別字,有效避免了在全域作用域新增變數。

關於模組,後面會進行更詳細的說明。


參考資料


上一篇
Day10 我也有自己的地盤啦!──區塊作用域
下一篇
Day12 提升:到底是在提什麼升什麼啦!
系列文
就是要搞懂 JavaScript 啦!73
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言