iT邦幫忙

2023 iThome 鐵人賽

DAY 24
0
自我挑戰組

30天前端網頁基礎觀念(HTML,CSS,Javascript,blender)系列 第 24

Day24 JavaScript lexical scope跟Closure 閉包

  • 分享至 

  • xImage
  •  

今天想跟大家分享lexical scopeclosure

JavaScript是一個lexical scope language(語彙範疇),意思是JS的變數作用域是在函數還沒執行前就決定好了。當我們執行一段程式時,JS引擎會先把整段程式碼看過一次,決定哪些函數可以使用哪些變數,接著才執行函數呼叫。

Lexical Scope (語彙範疇)

代表著區塊間的包裹關係,被包裹在內層的區塊可以保護自己的變數不被外層取用,相反的外層區塊的變數還是可以被內層區塊使用。

function Counter() {
        let count = 0
        return ++count;
}

console.log(Counter());  //1
console.log(Counter());  //1
console.log(Counter());  //1

有沒有發現這個結果不是我們想要的會往上遞增!因為count是區域變數你每一次呼叫就重新被宣告為0一次。

let count = 0
function Counter() {
        return ++count;
}

console.log(Counter());  //1
console.log(Counter());  //2
console.log(Counter());  //3

這個結果就可以達成我們要的目的因為這樣count是一個全域變數。但這會有一個問題存在!


Closure

如果今天你是跟同事一起寫程式碼的話你很難保證這個變數命名不會重複。這時候我們就會用一種叫做Closure的寫法。

我們就會把上方的程式碼改寫成這樣

function counter() {
	let count = 0;
    
	function innerCounter() {
	return ++count;
	}

	return innerCounter;
}

	let counterFun = counter();


console.log(counterFun());  //1
console.log(counterFun());  //2
console.log(counterFun());  //3

這樣子我們的count就不會是全域物件,就不會受到其他影響。

假設我們想在加一個計數器

function counter() {
	let count = 0;
    
	function innerCounter() {
	return ++count;
	}

	return innerCounter;
}

	let counterFun = counter();
    let counterFun2 = counter();


	console.log(counterFun()); //1
	console.log(counterFun2());  //1
	console.log(counterFun()); //2
	console.log(counterFun()); //3
	console.log(counterFun2());  //2
    console.log(counterFun2());  //3

這樣可以隨便你想怎麼調用這個計數器都不會受到影響。那如果我在外層在定義一個count

let count = 1;

function counter() {
let count = 0;

function innerCounter() {
		return ++count;
    }

        return innerCounter;
}

let counterFun = counter();
let counterFun2 = counter();

console.log(counterFun());
console.log(counterFun2());
console.log(counterFun());
console.log(counterFun());
console.log(counterFun2());
console.log(counterFun2());         

結果還是一樣,其實範圍鏈是在函式被定義的當下決定的,不是你呼叫的時候才去做定義。

順帶一提到如果在這個函式內找不到這個變數怎麼辦?

其實這不用擔心,他會在往外層去找看看是否有定義這個變數/images/emoticon/emoticon01.gif

所以如果今天不在counter這個函式內定義count變數的話結果會不一樣。

let count = 0;

function counter() {

function innerCounter() {
			return ++count;
    }

		return innerCounter;
}

let counterFun = counter();
let counterFun2 = counter();

console.log(counterFun()); //2
console.log(counterFun2()); //3
console.log(counterFun()); //4
console.log(counterFun()); //5
console.log(counterFun2()); //6
console.log(counterFun2()); //7

閉包的優點是變數僅存在於函式之中,如果匯出的方法沒有提供原始值,將無法用任何方式取得原始變數,如果需要刻意隱藏變數值,避免用戶透過其他工具或方式取得,閉包也會是一個好方法。

參考文獻

重新認識 JavaScript: Day 19 閉包 Closure
閉包,原來這就是閉包啊!

JavaScript - Lexical Scope


上一篇
Day23 JavaScript是傳值還是傳址
下一篇
Day25 JavaScript比較與運算符號
系列文
30天前端網頁基礎觀念(HTML,CSS,Javascript,blender)30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言