所謂的作用域,可以簡單理解為一個可以讀、寫的范圍(區域),有些js經驗的同學可能會說:"js沒有塊級作用域",js除了全局作用域外,只有函數可以創建作用域。作用域的一個好處就是可以隔離變量。
我們通過一些例子來幫助我們理解js中的作用域。
alert(a); var a = 1;
如果對作用域一點不了解的同學可能會說 alert的是1或者報錯;但實際上是undefined;
說到這裡,我們首先說一下js逐行解析代碼之前做的一些准備工作,
js在逐行讀代碼之前,會做一些“預解析”工作,會先提前找到一些”小東西”,當然”js解析器“不會隨便找一些數據的,它會根據var,function,參數來找。
”js解析器“它比較”懶“,在正式運行代碼之前都會給var聲明的變量賦值為undefined,也就是var a = undefined;會把整個函數看作一個代碼塊,不去管裡邊有多少代碼。參數等到後邊例子中會說。
當所有准備工作都做好後,“JS解析器”就開始逐行執行代碼了,現在我們來分析開始的這個例子就很容易明白為什麼是undefined了。
再來看下邊這個例子
alert(a); var a = 1; alert(a); var a = 2; alert(a);
我們來一點點分析這個
首先 ”預解析“: 解析器會找var
讀到第二行時 a = undefined;
讀到第四行時 依然 a = undefined;
正式逐行執行代碼:
第一行 alert:undefined
第二行 a = 1;
第三行 alert:1;
第五行 alert:2
接著看下邊這個例子
alert(a); var a = 1; alert(a); function a (){ alert(2); } alert(a); var a = 3; alert(a); function a (){ alert(4); } alert(a);
我們依然來一點點分析這個
首先 ”預解析“: 解析器會找var function;
讀到第二行時 a = undefined;
讀到第四行時 a = function a (){ alert(2);} //所有的函數,在正式運行代碼之前,都是整個函數塊;變量遇到重名的,只留一個變量,如果變量和函數重名,就只留下函數。
讀到第六行時,a = function a (){ alert(2);}
讀到第八行時,a = function a (){ alert(4);}
正式逐行執行代碼:
第一行 alert: function a (){ alert(4);}
第二行 a = 1; //表達式可以修改預解析的值!
第三行 alert:1;
第四行 函數沒有調用,略過;
第五行 alert:1;
第六行 a = 3;
第七行 alert:3
第八行 函數沒有調用,略過;
第九行 alert:3
如圖所示:
繼續看例子:
var a = 1; function fn1(){ alert(a); //undefined var a = 2; } fn1(); alert(a); //1
首先 ”預解析“: 解析器會找var function
讀到第一行時 a = undefined;
讀到第二行時 fn1 = function fn1 (){alert(2);var a = 2;}
正式逐行執行代碼: 第一行 a = 1;
第六行 函數調用,進入函數作用域 在函數作用域內依舊是先預解析,再逐行執行
函數內預解析:a = undefined;
執行:alert:undefined;
a = 2; //此時的a僅為函數作用域中的a,不會影響全局中的a
函數執行完畢,回到全局作用域;
第七行 alert:1;
繼續:
var a = 1; function fn1(){ alert(a); //1 a = 2; } fn1(); alert(a); //2
這個例子上邊那個例子唯一的區別就是函數中的a沒有var,只分析其中關鍵的地方
在函數作用域中 第三行alert(a),由於函數中沒有var a,所以"解析器"會到函數的作用域的上一級作用域去尋找a(作用域上下級關系的確定就看函數是在哪個作用域下創建的,在哪個作用域下創建,就是哪個作用域的下級),此時函數的上一級是全局作用域,在全局作用域中,a = 1,所以此時第三行 alert:1,接著第四行,a = 2賦值,依然是函數作用域中沒有a, 所以在上一級作用域,也就是全局作用域中找到a,修改全局作用域中的a, 所以會使全局作用域中的a = 2, 因此第七行 alert:2;
這點要理解清楚,注意有無var的區別。
接著來:
var a = 1; function fn1(a){ alert(a); //undefined a = 2; } fn1(); alert(a); // 1
這個例子和上一個的區別就是多了個參數,參數的作用相當於局部變量,也就是在函數中預解析會有var a = undefined,所以第三行 alert:undefined,第四行 a = 2 改的是函數作用域中的a,不影響全局中的a,第七行alert:1;
接著:
var a = 1; function fn1(a){ alert(a); // 1 a = 2; } fn1(a); alert(a); // 1
這個例子又與上一個有些區別,在第六行函數調用時傳了個實參進去,第六行函數實參的a是全局變量a = 1的1,函數執行時,第二行 a = 1,所以第三行alert:1,第七行alert:1。
注意這幾個例子之間的區別,別混淆了。
再來一個:
var a = 1; function en(){ var a = 2; fn(); } function fn(){ alert(a); //1 } en();
fn中的a未聲明,要到創建這個函數的那個作用域中取值——是“創建”,而不是“調用”這個函數的作用域中。
PS:JavaScript中的作用域和上下文概念
javascript中的作用域(scope)和上下文(context)是這門語言的獨到之處,這部分歸功於他們帶來的靈活性。每個函數有不同的變量上下文和作用域。這些概念是javascript中一些強大的設計模式的後盾。然而這也給開發人員帶來很大困惑。下面全面揭示了javascript中的上下文和作用域的不同,以及各種設計模式如何使用他們。
上下文 vs 作用域
首先需要澄清的問題是上下文和作用域是不同的概念。多年來我注意到許多開發者經常將這兩個術語混淆,錯誤的將一個描述為另一個。平心而論,這些術語變得非常混亂不堪。
每個函數調用都有與之相關的作用域和上下文。從根本上說,范圍是基於函數(function-based)而上下文是基於對象(object-based)。換句話說,作用域是和每次函數調用時變量的訪問有關,並且每次調用都是獨立的。上下文總是關鍵字 this 的值,是調用當前可執行代碼的對象的引用。
以上所述是小編給大家介紹的javascript中的作用域(推薦),希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對網站的支持!