變量(變量作用於又稱上下文)和函數生效(能被訪問)的區域或集合。換句話說,作用域決定了代碼區塊中變量和其他資源的可見性。我們來看個例子:
function myFunction() { let inVariable = "函數內部變量"; } myFunction();//要先執行這個函數,否則根本不知道裡面是啥 console.log(inVariable); // Uncaught ReferenceError: inVariable is not defined複製代碼
從上面的例子可以看出作用域的概念,我們創建了一個函數myFunction,在函數內部創建了一個變量inVariable,當我們在全局訪問這個變量沒,系統會直接報錯。這就說明我們在全局是無法獲取到(閉包除外)函數內部的變量。我們可以這樣理解: 作用域就是一個獨立的地盤,讓變量不會外泄、暴露出去。也就是說作用域最大的用處就是隔離變量,不同作用域下同名變量不會有衝突。
在代碼中任何地方都能訪問到的對象擁有全局作用域,一般來說以下幾種情形擁有全局作用域
let outVariable = "我是外部變量"; function outFunction() { let inVariable = "我是內部變量"; function inFunction() { // 我是內部函數 console.log(inVariable); } inFunction(); } console.log(outVariable); // 我是外部變量 outFunction(); // 我是內部變量 console.log(inVariable); //inVariable is not defined inFunction(); //inFunction is not defined
function outFunction() { variable = "未定義直接賦值的變量"; var inVariable = "我是內部變量"; } outFunction();//要先執行這個函數,否則根本不知道裡面是啥 console.log(variable); //未定義直接賦值的變量,這裡會有個「變量提升」 console.log(inVariable); //inVariable is not defined
上面的代碼中在函數outFunction內部variable未定義就先賦值,這裡會將variable變量提升到全局作用域。 變量提升 是發生在函數的預編譯階段,它的意思就是說即任何變量,如果未經聲明就賦值,此變量就為全局對象所有。 變量提升 也叫 暗示全局變量 。
在講作用域鏈之前我們首先要知道一個概念[[scope]]
下面我們通過一段代碼詳細介紹一下作用域
function a() { function b() { var b = 234; } var a = 123; b(); } var gloab = 100; a(); console.log(gloab); console.log(b); console.log(a)
我們都知道JavaScript代碼在執行前都會有一個過程叫做 預編譯 ,前面我們提到過。接下來我們一步一步分析。
第一步:a 函數定義
我們可以從上圖中看到,a 函數在被定義時,a函數對象的屬性[[scope]]作用域指向他的作用域鏈scope chain,此時它的作用域鏈的第一項指向了GO(Global Object)全局對象,我們看到全局對象上此時有5個屬性,分別是this、window、document、a、glob。
第二步:a 函數執行
當a函數被執行時,此時a函數對象的作用域[[scope]]的作用域鏈scope chain的第一項指向了AO(Activation Object)活動對象,AO對象里有4個屬性,分別是this、arguments、a、b。第二項指向了GO(Global Object),GO對象里依然有5個屬性,分別是this、window、document、a、golb。
第三步:b 函數定義
當b函數被定義時,此時b函數對象的作用域[[scope]]的作用域鏈scope chain的第一項指向了AO(Activation Object)活動對象,AO對象里有4個屬性,分別是this、arguments、a、b。第二項指向了GO(Global Object),GO對象里依然有5個屬性,分別是this、window、document、a、golb。
第四步:b 函數執行
當b函數被執行時,此時b函數對象的作用域[[scope]]的作用域鏈scope chain的第一項指向了AO(Activation Object)活動對象,AO對象里有3個屬性,分別是this、arguments、b。第一項指向了AO(Activation Object)活動對象,AO對象里有4個屬性,分別是this、arguments、a、b。第二項指向了GO(Global Object),GO對象里依然有5個屬性,分別是this、window、document、a、golb。
以上就是上面代碼執行完之後的結果。當我們訪問其中的變量時,要從作用域鏈的底部向上查找。
首先查找變量glob,作用域鏈第一項上沒有找到繼續在第二項查找,依然沒有,第三項在GO中查找到了,最終的值是100;
其次查找變量b,作用域鏈的第一項上查找,發現存在變量b,最終變量的b的值是234;
最後查找變量a,作用域鏈第一項上沒有找到繼續在第二項查找,發現存在變量a,最終變量a的值是123。
至此JavaScript作用域、作用域鏈基本上就介紹完了,後續我會給大家介紹一下前面說到的 閉包 和 預編譯 。如果大家還有其他不同的理解或者是建議,還請在下發留言。如果有理解的不正確的地方還請多多指教。