結(jié)合代碼圖文講解JavaScript中的作用域與作用域鏈
先上三段說(shuō)明作用域的代碼
//==========例1==========
var scope='global';
function fn(){
alert(scope);
var scope='local';
alert(scope);
}
fn(); //輸出結(jié)果?
alert(scope);//輸出結(jié)果?
//===========例2==========
var scope='global';
function fn(){
alert(scope);
scope='local';
alert(scope);
}
fn(); //輸出結(jié)果?
alert(scope);//輸出結(jié)果?
//===========例3=========
var scope='global';
function fn(scope){
alert(scope);
scope='local';
alert(scope);
}
fn(); //輸出結(jié)果?
alert(scope);//輸出結(jié)果?
這三段代碼只有小許差異,但結(jié)果缺截然不同,例1分別輸出[undefined , local , global],例2分別輸出[global , local , local],例3結(jié)果輸出[undefined , local , global],如果不能答對(duì)說(shuō)明你對(duì)javascript的作用域特性還未理解透徹。
什么是作用域?
也許有人會(huì)問(wèn):變量a的作用域是什么?一會(huì)兒又問(wèn):函數(shù)a的作用域是什么?變量和函數(shù)的作用域分別是啥玩意?
我們先來(lái)看看“作用域”是什么意思,“作用域”拆開(kāi)來(lái)就是“作用”和“域”
- 作用:訪問(wèn)、操作、調(diào)用……
- 域:區(qū)域、范圍、空間……
作用域就是變量和函數(shù)的可訪問(wèn)范圍,或者說(shuō)變量或函數(shù)起作用的區(qū)域。
1.javascript函數(shù)的作用域:
函數(shù)內(nèi)的區(qū)域,就是這個(gè)函數(shù)的作用域,變量和函數(shù)在這個(gè)區(qū)域都可以訪問(wèn)操作。最外層函數(shù)外的區(qū)域叫全局作用域,函數(shù)內(nèi)的區(qū)域叫局部作用域。
2.javascript變量的作用域:
在源代碼中變量所在的區(qū)域,就是這個(gè)變量的作用域,變量在這個(gè)區(qū)域內(nèi)可以被訪問(wèn)操作。在全局作用域上定義的變量叫全局變量,在函數(shù)內(nèi)定義的變量叫局部變量。
簡(jiǎn)單地理解,JS源代碼被函數(shù){ }劃分成一塊一塊的區(qū)域,這些區(qū)域換個(gè)身份就是某函數(shù)或某變量的作用域,變量的作用域和函數(shù)的作用域在源代碼中有可能指的是同一塊區(qū)域。
作用域鏈
作用域鏈(Scope Chain)是javascript內(nèi)部中一種變量、函數(shù)查找機(jī)制,它決定了變量和函數(shù)的作用范圍,即作用域,理解作用域鏈的作用原理,上一篇文章的三個(gè)例子也就能理解了,從而知其然也知其所以然。
作用域鏈?zhǔn)荅CMAScript-262說(shuō)明文檔中的概念,javascript引擎是按ECMAScript-262說(shuō)明文檔去實(shí)現(xiàn)的,了解javascript引擎的工作原理有利于我們理解javascript的特性,但絕大多數(shù)js程序員不會(huì)去了解非常底層的技術(shù),所以閱讀ECMAScript-262說(shuō)明文檔,我們可以有一個(gè)直觀的方式去模擬javascript引擎的工作原理。
本文將通過(guò)1999年的ECMAScript-262-3th第三版來(lái)說(shuō)明作用域鏈的形成原理,將會(huì)介紹執(zhí)行環(huán)境,變量對(duì)象和活動(dòng)對(duì)象,arguments對(duì)象,作用域鏈等幾個(gè)概念。2009年發(fā)布了ECMAScript-262-5th第五版,不同的是取消了變量對(duì)象和活動(dòng)對(duì)象等概念,引入了詞法環(huán)境(Lexical Environments)、環(huán)境記錄(EnviromentRecord)等新的概念,所以?xún)蓚€(gè)版本的概念不要混淆了。
1.執(zhí)行環(huán)境(Execution Contexts)
執(zhí)行環(huán)境(Execution Contexts)也被翻譯為執(zhí)行上下文,當(dāng)解析器進(jìn)入ECMAScript的可執(zhí)行代碼,解析器就進(jìn)入一個(gè)執(zhí)行環(huán)境,活動(dòng)的執(zhí)行環(huán)境組成一個(gè)邏輯上的棧,在這個(gè)邏輯棧頂部的執(zhí)行環(huán)境是當(dāng)前運(yùn)行的執(zhí)行環(huán)境。
注:ECMAScript中有三種可執(zhí)行代碼,Global、Function和Eval,全局環(huán)境即是Global可執(zhí)行代碼,函數(shù)即是Function可執(zhí)行代碼。邏輯棧是一種特殊的數(shù)據(jù)存儲(chǔ)格式,特點(diǎn)是‘先進(jìn)后出,后進(jìn)先出',添加數(shù)據(jù)會(huì)先壓入邏輯棧頂部,刪除數(shù)據(jù)必須先從頂部開(kāi)始刪除。

變量對(duì)象(Variable Object)、活動(dòng)對(duì)象(Activation Object)和Arguments對(duì)象(Arguments Object)
每個(gè)執(zhí)行環(huán)境都有一個(gè)與之關(guān)聯(lián)的變量對(duì)象,當(dāng)解析器進(jìn)入執(zhí)行環(huán)境時(shí),就會(huì)創(chuàng)建一個(gè)變量對(duì)象,變量對(duì)象保存著在當(dāng)前執(zhí)行環(huán)境中聲明的變量和函數(shù)的引用。
變量對(duì)象是一個(gè)抽象的概念,在不同的執(zhí)行環(huán)境中,變量對(duì)象有不同的身份,在解析器進(jìn)入任何執(zhí)行環(huán)境之前,就已經(jīng)創(chuàng)建了一個(gè)Global對(duì)象,當(dāng)解析器進(jìn)入全局執(zhí)行環(huán)境時(shí),Global對(duì)象就充當(dāng)變量對(duì)象,當(dāng)解析器進(jìn)入一個(gè)函數(shù)時(shí),就會(huì)創(chuàng)建一個(gè)活動(dòng)對(duì)象充當(dāng)變量對(duì)象。
2.解析器處理代碼時(shí)的兩個(gè)階段
我們都知道javascript解析器是一段一段解析處理代碼的,為毛?這就要涉及解析器處理代碼時(shí)的兩個(gè)階段,解析代碼和執(zhí)行代碼。
當(dāng)解析器進(jìn)入執(zhí)行環(huán)境時(shí),變量對(duì)象就會(huì)添加執(zhí)行環(huán)境中聲明的變量和函數(shù)作為它的屬性,這就意味著變量和函數(shù)在聲明之前已經(jīng)可用,變量值為undefined,這就是變量和函數(shù)聲明提升(Hoisting)的原因,與此同時(shí)作用域鏈和this確定,此過(guò)程為解析階段,俗稱(chēng)預(yù)解析。接著解析器開(kāi)始執(zhí)行代碼,為變量添加相應(yīng)值的引用,得到執(zhí)行結(jié)果,此過(guò)程為執(zhí)行階段。
舉兩個(gè)好吃的栗子:
var a=123;
var b="abc";
function c(){
alert('11');
}
上述全局環(huán)境中的代碼解析執(zhí)行后,會(huì)將Global對(duì)象作為變量對(duì)象,保存以下數(shù)據(jù)。

function testFn(a){
var b="123";
function c(){
alert("abc");
}
}
testFn(10);
當(dāng)解析器進(jìn)入函數(shù)執(zhí)行環(huán)境時(shí),則會(huì)創(chuàng)建一個(gè)活動(dòng)對(duì)象作為變量對(duì)象,活動(dòng)對(duì)象還會(huì)創(chuàng)建一個(gè)Arguments對(duì)象,arguments對(duì)象是一個(gè)參數(shù)集合,用來(lái)保存參數(shù),這就是我們寫(xiě)函數(shù)時(shí)可以使用arguments[0]等來(lái)使用參數(shù)的原因。

3.作用域鏈(Scope Chain)
每個(gè)執(zhí)行環(huán)境都有一個(gè)與之關(guān)聯(lián)的作用域鏈,當(dāng)解析器進(jìn)入執(zhí)行環(huán)境時(shí)被定義,作用域鏈?zhǔn)且粋€(gè)對(duì)象列表,用來(lái)檢索各個(gè)變量對(duì)象中的變量和函數(shù),這樣可以保證執(zhí)行環(huán)境有權(quán)訪問(wèn)哪些變量和函數(shù),舉個(gè)栗子。
var a='123';
function testFn(b){
var c='abc';
function testFn2(){
var d='efg';
alert(a);
}
testFn2();
}
testFn(10);
testFn2內(nèi)未聲明變量a,為什么testFn2能調(diào)用全局變量a?整個(gè)過(guò)程是怎么發(fā)生的呢?請(qǐng)看下圖。

當(dāng)解析器進(jìn)入全局執(zhí)行環(huán)境時(shí),調(diào)用變量和函數(shù)時(shí)只在Global對(duì)象中查找。
當(dāng)解析器進(jìn)入testFn函數(shù)執(zhí)行環(huán)境時(shí),函數(shù)內(nèi)部屬性[[scope]]中首先填入Global對(duì)象,然后將testFn活動(dòng)對(duì)象添加到Global對(duì)象之前,形成一個(gè)作用域鏈。

當(dāng)解析器進(jìn)入testFn2函數(shù)執(zhí)行環(huán)境時(shí),函數(shù)內(nèi)部屬性[[scope]]首先填入父級(jí)的作用域鏈,然后再將當(dāng)前的testFn2活動(dòng)對(duì)象添加到作用域鏈的前端,形成一個(gè)新的作用域鏈。
testFn2調(diào)用變量a時(shí),首先在當(dāng)前的testFn2活動(dòng)對(duì)象中查找,如果沒(méi)有找到就順著作用域鏈向上,在testFn活動(dòng)對(duì)象中查找變量a,如果沒(méi)有找到再順著作用域鏈向上查找,直到在最后Global對(duì)象中找到為止,否則報(bào)錯(cuò)。所以函數(shù)內(nèi)部可以調(diào)用外部環(huán)境的變量,外部環(huán)境不能調(diào)用函數(shù)內(nèi)部的變量,這就是作用域特性的原理。
相關(guān)文章
javascript對(duì)話框使用方法(警告框 javascript確認(rèn)框 提示框)
javascript對(duì)話框使用方法,有警告框、確認(rèn)框、提示框的使用方法和語(yǔ)法,大家參考使用吧2014-01-01
JavaScript中用sort()方法對(duì)數(shù)組元素進(jìn)行排序的操作
這篇文章主要介紹了JavaScript中用sort()方法對(duì)數(shù)組元素進(jìn)行排序的操作,是JS入門(mén)學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-06-06
javascript如何創(chuàng)建表格(javascript繪制表格的二種方法)
利用js來(lái)動(dòng)態(tài)創(chuàng)建表格有兩種格式,appendChild()和insertRow、insertCell()。兩種方式其實(shí)差不多,但第一種有可能在IE上有問(wèn)題,所以推薦大家使用第二種方法,看下面的解決和使用方法2013-12-12
javascript下for循環(huán)用法小結(jié)
javascript下for循環(huán)用法小結(jié)...2007-07-07
JavaScript數(shù)據(jù)類(lèi)型轉(zhuǎn)換
本文詳細(xì)講解了JavaScript實(shí)現(xiàn)數(shù)據(jù)類(lèi)型轉(zhuǎn)換的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-03-03
ES6下javascript解構(gòu)賦值常見(jiàn)用法總結(jié)
這篇文章主要介紹了在ES6下javascript賦值常見(jiàn)用法總結(jié),需要的朋友可以參考下2022-01-01
關(guān)于JavaScript對(duì)象類(lèi)型之Array及Object
這篇文章主要介紹了關(guān)于JavaScript對(duì)象類(lèi)型之Array及Object,Array 類(lèi)型是 ECMAScript 中最常用的類(lèi)型了。而且,ECMAScript 中的數(shù)組與其他多數(shù)語(yǔ)言中的數(shù)組有著相當(dāng)大的區(qū)別,需要的朋友可以參考下2023-05-05
JavaScript中操作字符串之localeCompare()方法的使用
這篇文章主要介紹了JavaScript中操作字符串之localeCompare()方法的使用,是JS入門(mén)學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-06-06

