在預解析原理(一)中我們簡單介紹了一下JS的解析過程,這篇文章會對這個過程進行深入的分析。 在這個過程中首先需要明白三個概念: 1、全局作用域:也就是全局變數聲明在函數之外的變數預設作用整個工程; 2、局部作用域:聲明在函數體中的變數,並且只能在當前函數體內訪問,如:function(){var a ...
在預解析原理(一)中我們簡單介紹了一下JS的解析過程,這篇文章會對這個過程進行深入的分析。
在這個過程中首先需要明白三個概念:
1、全局作用域:也就是全局變數聲明在函數之外的變數預設作用整個工程;
2、局部作用域:聲明在函數體中的變數,並且只能在當前函數體內訪問,如:function(){var a = 0;};
3、作用域鏈:JavaScript中所有的變數都是存在於某一個作用域中的,除了全局作用域, 每一個作用域都是存在於某個作用域中的,在試圖訪問一個變數時JS引擎會從當前作用域開始向上查找直到找到全局作用域時停止,這個執行過程的有序集合就是作用域鏈;
明白了這三個概念我們就開始學習下邊的內容:
1、全局作用域:
首先看一個例子
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title></title>
<script>
alert(a);
</script>
<script>
var a = 1;
</script>
</head>
</html>
在這個例子中有兩個script,這兩個script均是全局的作用域,但是當把alert(a);與var a = 1;分別寫在兩個不同的域中,就會出現一些無法想象的問題,例如:報錯。
在這個過程中瀏覽器提示a沒有被定義,是因為JS在執行的過程中是自上而下進行的,它在第一個作用域中沒有找到a導致報錯,然後程式終止了。
但是如果把上下順序進行對調就會產生不一樣的效果:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title></title>
<script>
var a = 1;
</script>
<script>
alert(a);
</script>
</head>
</html>
這是因為,當解讀到第一個域的時候,a作為變數被保留在“倉庫”中了,然後自上而下執行到第二個域的時候就可以正常執行;
所以,當要引入一些JS文件的時候儘量寫在上邊,否則會出現意想不到的情況(JQ等進行處理的不算在內)。
2、局部作用域
代碼:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title></title>
<script>
var a = 1;
function fn1 () {
alert(a);
var a = 2;
}
fn1();
alert(a);
</script>
</head>
</html>
首先不管是全局作用域還是局部的作用域在執行的時候都會進行前文中講的那兩部:1、JS預解析;2、逐行解讀代碼
所以以上的代碼執行原理為:
var a = 1; 預解析找到var a被賦值為未定義;
function fn1 (){ alert(a); var a = 2;} 由於這是一個函數所有預解析的時候直接存為函數本身,也就是function fn1 (){ alert(a); var a = 2;} ;
然後由於沒有其他的關鍵字以及函數,所以預解析完成執行下一步,
1、當程式獨到var a = 1;的時候,“倉庫”裡邊的a = 未定義,就會被替換成 a = 1;
2、執行function fn1 (){ alert(a); var a = 2;}的時候由於它既不是表達式又沒有調用所有沒有任何變化;
3、當執行到fn1();的時候,發生了函數調用,同時也觸發了局部作用域:
1、執行JS預解析:(局部作用域的解析範圍僅限於局部作用域內,所以他的解析行為如下)
找到var關鍵字,把a儲存為未定義,由於函數內沒有其他的內容了所以預解析結束,
預解析的結果:a = undefined ;
2、逐行解讀代碼:
局部作用域在進行這一步的時候會優先從內部進行查找,由於預解析的時候函數內是有a = 未定義的,然後會彈出 undefined,(這裡有一個需要註意的地方函數內的 a 與 函數外的a沒有任何聯繫。)最後執行var a = 2;的時候出現了表達式,則第一步的預解析結果就會由a = undefined變為a = 2;然後第二步執行完畢。
4、執行alert(a);由於這段代碼是在函數外邊,所以會調用全局變數var a = 1,最終彈出的結果就是1;
3、作用域鏈
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title></title>
<script>
var a = 1;
function fn1 () {
alert(a);
a = 2;
}
fn1();
alert(a);
</script>
</head>
</html>
全局作用域與局部作用域的原理在此就不在進行解釋了,如果有不明白的地方可以參考上邊的內容。
當局部作用域在解析的時候由於函數內沒有內容,這個時候就會觸發作用域鏈,返回父級進行查找,所以當代碼執行到alert(a);時會彈出 1 ;
然後逐行解讀到a = 2;的時候函數內的變數會把全局變數修改為2,最後在執行alert(a);時,會彈出2。
那麼加入參數之後會怎麼樣呢?
測試一下
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title></title>
<script>
var a = 1;
function fn1 (a) {
alert(a);
a = 2;
}
fn1();
alert(a);
</script>
</head>
</html>
當函數內進行預解析的時候由於函數有參數a,所以預解析的結果會變成undefined,執行函數內的alert(a);會彈出undefined,
在執行a = 2時,把2賦值給a,所以此時的局部變數有undefined變成了2;然後局部代碼執行完畢。
當執行到全局的alert時,由於此時並不在函數內所以會彈出1.