1. 程式人生 > >JavaScript踩坑筆記07---作用域鏈、動態作用域、靜態作用域、詞法作用域

JavaScript踩坑筆記07---作用域鏈、動態作用域、靜態作用域、詞法作用域

JavaScript踩坑筆記07---作用域鏈、動態作用域、靜態作用域、詞法作用域

作用域鏈:

函式在定義時,不光確定了它內部的作用域,還確定了它外部的作用域,也就是作用域鏈。
舉例說明。

// 全域性變數num,boo
var num = 1;
var boo;

function fn1() {

	function fn2() {

		function fn3()
{ // 依次從父級作用域找同名自由變數 console.log(num); // 1 // 直到全域性作用域下也沒找到該變數,證明該變數未宣告,程式報錯 console.log(str); // ReferenceError: str is not defined // 在全域性作用域下找到變數boo,雖然已宣告,但是並沒有初始化,所以未定義 console.log(boo); // undefined } fn3(); } fn2(); } fn1();

在上述例子中,我定義了三個函式,並且依次巢狀。在函式fn3中並沒有變數num,那麼就只能找來自父作用域的同名自由變數,父作用域也沒找到,再向上一級父作用域找同名的自由變數,最終找到變數num,如果最終沒有找到該變數,那就證明該變數未宣告,或未定義。這就是作用域鏈。
所以說找一個變數,只需要向其父作用域逐級向上找,就可以了嗎?
JavaScript的作用域可沒這麼簡單,要分清函式被呼叫時的作用域和函式定義時的作用域。下面動態作用域和靜態作用域會說明。

動態作用域:

動態作用域指的是,在呼叫某個變數時,會從當前作用域逐級向上查詢。
如果在當前作用域找到,就呼叫該變數,如果沒找到,就繼續向父級作用域查詢,以此類推。
如果一直查詢到最外層的全域性作用域,都沒有找到該變數,那麼就表明沒有該變數。
舉例說明。

// 父級作用域變數num1
var num1 = 1;

function fn() {
	// 當前作用域變數num2
	var num2 = 2;

	// 在當前作用域沒有找到變數num1,向父級作用域查詢,在父級作用域找到變數num1,直接呼叫
	console.log(num1); // 1
	// 在當前作用域找到變數num2,直接呼叫
console.log(num2); // 2 // 在當前作用域沒有找到變數num3,向父級作用域查詢,在父級全域性作用域也沒找到變數num3,證明該變數未定義 console.log(num3); // ReferenceError: num3 is not defined } fn();

靜態作用域、詞法作用域:

靜態作用域,又叫作詞法作用域,函式在定義的時候,就已經確定了函式體內部自由變數的作用域。
JavaScript採用的是靜態作用域。
舉例說明。

// 全域性變數num
var num = "1";

function show() {

	function fn1() {
		console.log(num);
	}

	function fn2() {
		// 區域性變數num
		var num = 2;
		fn1();
	}

	fn2();
}

show(); // 1

當執行函式fn時,會首先在函式show內部查詢變數num,如果找到,則直接呼叫,如果沒找到,就到函式show定義的作用域下查詢,如果找到,則直接呼叫,如果沒找到,則會逐級向上查詢。

動態作用域與靜態作用域的區別:

動態作用域的查詢規則是程式執行時的函式呼叫順序。
靜態作用域的查詢規則是距離當前作用域(宣告的位置)最近的外層作用域中的同名變數。

《JavaScript權威指南》經典例子:

以下例子為《JavaScript權威指南》中經典的一個例子,基本網上的每個大佬都會提及。
例一:

var scope = "global scope";

function checkScope() {
	var scope = "local scope";

	function fn() {
		return scope;
	}
	return fn();
}
checkScope(); // local scope

分析:
在函式checkScope中定義了函式fn,並將函式fn的返回值當作函式checkScope的返回值返回。
首先在函式fn的內部作用域中找變數scope,沒找到變數,所以在函式fn當前所在的作用域下找變數scope,找到變數,直接返回,所以返回的結果是local scope。
例二:

var scope = "global scope";

function checkScope() {
	var scope = "local scope";

	function fn() {
		return scope;
	}
	return fn;
}
checkScope()(); // local scope

分析:
在函式checkScope中呼叫函式fn,函式fn同作用域下定義了變數scope,所以輸出的是local scope。
在函式checkScope中定義了函式fn,並將函式fn當作函式checkScope的返回值返回,此時函式checkScope返回的是一個函式物件,不管這個返回的函式在哪裡執行,當他定義時,他的作用域就已經確定了。
所以首先在函式fn的內部作用域中找變數scope,沒找到變數,所以在函式fn當前所在的作用域下找變數scope,找到變數,直接返回,所以返回的結果還是local scope。


個人學習總結,歡迎批評指正