理解 javascript 作用域

對於作用域的話,我覺得就是一些變量能夠被訪問或者操作的一個區域。如果超過這個區域的話,它似乎就沒有了利用價值了。

作用域前的準備

在瞭解作用域前,我們來說一下題外話。

1.可執行代碼:有三種:(1)全局代碼  (2)函數代碼  (3)eval函數

2.執行環境:每當javascript解析器進入可執行代碼的時候,它就會爲它創建一個執行環境,也稱執行上下文

3.變量對象:執行環境中的變量和函數都是保存到整個對象中的,還有一點就是我們不能用javascript代碼去訪問整個對象,但是解析器在訪問的時候會在後臺使用它。

4.活動對象:當執行環境是函數的時候,那麼函數被調用的時候,該活動對象也就是被創建,並且把它作爲變量對象。

進入這篇文章的作用域

上面爲了作用域這個概念鋪墊了很久了。那好,我們來講下作用域這個似乎很抽象的概念。每當javascript解析器進入到一個執行環境的時候,就會爲變量對象創建一個作用域鏈。作用域鏈的前端總是該執行環境所對應的變量對象,然後作用域鏈下一個變量對象來着外部的環境,而下一個變量對象就來着下一個外部的環境,一直到全局執行環境就停止。(摘自《javascript高級程序設計》)其實也就是外部的執行環境就是它所包含的執行環境的父環境。然後在尋找變量的時候會沿着作用域一級一級去尋找,從作用域鏈的前端到全局執行環境,如果找不到就報錯,如果找到就不向下尋找了。

我們先來看看一個例子來了解上面很抽象的解釋:

var name = 'monkind';
(function fun(){
 alert(name);//monkind
})();
上面彈出來的就是monkind不是undefined,記住這個,下面待會有一個跟它很相似的。用上面的理論我們可以理解:在全局代碼中創建一個執行環境和作用域鏈,然後執行環境相關聯的變量對象把name還有fun函數設置爲屬性並賦值。然後就是自執行函數fun的運行,也創建一個執行環境,它會創建活動對象,再把當前的執行環境放在作用域鏈的最前端,外面就是全局執行環境。當要訪問name這個變量的時候,javascript解析器會沿着作用域訪問下去,在fun函數中找不到,就只能到全局執行環境也就是window找了。

javascript沒有塊級作用域

什麼是塊級作用域,就是用花括號封閉的代碼有自己的執行環境和作用域,一旦離開花括號的話就會自動銷燬裏面的變量等,但是不要跟函數作用域混淆,塊級作用域不包含函數的,只有if和for語句。但是偏偏javascript就要搞特殊。來,我們來看下面的代碼:

if (true) {
	var j = '19';
	for (var k = 0; k < 10; k++) {
	}
}
console.log(k + '...last');
console.log(j + '....j');

如果它沒有塊級作用域的話,那麼他的k和j的值是什麼?

其實上面的代碼相當於這樣子寫的:

var j = '19', k = 0;
if (true) {
	for (; k < 10; k++) {
	}
}
console.log(k + '...last');
console.log(j + '....j');

我們再看下下面的代碼,它彈出來會是哪個值呢?

if (true) {
	var j = '19';
	for (var k = 0; k < 10; k++) {
	}
}
function fun(){
 alert(k);
}
fun();
沒錯,它還是彈出來10,爲什麼呢?這個就留給讀者慢慢琢磨了,原理都在上面了。

javascript詞法作用域

對於一些新的專業名稱,我第一反應就是它究竟是什麼?不喜歡太專業太權威的解釋,用比較淳樸的語言或者舉個例子來解釋容易讓菜鳥懂呀。對於詞法作用域就是javascript執行環境的作用域是從定義它的時候就確定下來了,而不是在它運行的時候。作爲第一次看見這個解釋之後我還是很懵懂呀,不知道你們會不會呀,我覺得應該舉一兩個例子來解釋下。

var name = 'monkind';
function myName() {
	alert(name);
}

function callMyName() {
	var name = 'zkh';
	myName();
}
callMyName();

那麼究竟會彈出來那個name呢?是monkind還是zkh呢?事實上彈出的是monkind。那麼爲什麼呢?我們要知道myName這個函數在全局執行環境定義的,所以它的作用域就是全局執行環境的,雖然它執行的是在callMyName函數中,但是記住javascript是詞法作用域。那麼當然不能訪問callMyName函數中的name啦,只能訪問全局環境的name也就是monkind。

迴應上面的那句"記住這個,下面待會有一個跟它很相似的",我們再來看下一個例子,也是很經典的例子。

var name = 'monkind';
(function fun(){
 alert(name);
 var name = 'zkh';
 alert(name);
})();

第一個彈出的窗口是"undefined",第二個是"zkh";那麼爲什麼第一個alert不去訪問全局的name呢?我們首先要去了解一個就是:javascript引擎會先對執行環境中的變量和函數進行定義,如果沒有賦值的話就設置爲undefined。所以fun函數中的name對該函數是可見的,在詞法分析後的構造作用域鏈中,會把name放在fun函數的執行環境中。所以第一個訪問就是還沒賦值的undefined,要訪問第二個name的時候,name已經賦值給"zkh"了,大家可以用chrome開發者工具的斷點去看。


這個是在第一個alert斷點的情況。

感謝那些好的文章還有書籍,給我寫作的動力。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章