摘要:首先,在創(chuàng)建函數(shù)時,作用域鏈內(nèi)就會先填入對象,圖片只例舉了全部變量中的一部分。然后,解釋器進入函數(shù)的執(zhí)行環(huán)境,同樣的,首先填入父級的作用域鏈,就是的,包括了對象活動對象。之后再把的活動對象填入到作用域鏈最頂部,這就是的作用域鏈了。
之前學習JS函數(shù)部分時,提到了作用域這一節(jié),但是因為使用材料書不同,今天在讀博客的時候發(fā)現(xiàn)其實還有一個知識點即作用域鏈,所以來寫一些個人理解和認識加深記憶。
引用:JavaScript中的作用域鏈和閉包
JS的作用域和作用域鏈
結(jié)合代碼圖文講解JavaScript中的作用域與作用域鏈(主要)
先上考試代碼:
/==========例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é)果?
我當時做時候,卡在了第三題,后面明白了。
例1:只要記得變量聲明提升,例1應該沒什么問題,由于var變量聲明提前,所以當調(diào)用fn()時,第一個alert應該彈出undefined,之后賦值,再alert出"local"。而函數(shù)外的alert之前調(diào)用全局變量scope,彈出"global"。
var scope = "global"; function fn() { var scope; alert(scope); // undefined scope = "local"; alert(scope); // local }; fn(); alert(scope); // global
例2:由于函數(shù)內(nèi)沒有var聲明變量,所以函數(shù)內(nèi)的scope指向的是全局變量scope,那alert當然是全局變量的值啦——"global",之后賦值,再次alert,彈出"local"。此時全局變量scope已經(jīng)被重新賦值,所以函數(shù)外的alert彈出"local"。
例3:這里先不提,后面就OK了。當時我不知道如果傳入實參在函數(shù)內(nèi)是以什么方式存在,不傳入值為多少等。
板書ing
作用域鏈:作用域鏈(Scope Chain)是javascript內(nèi)部中一種變量、函數(shù)查找機制,它決定了變量和函數(shù)的作用范圍,即作用域,理解作用域鏈的作用原理,上一篇文章的三個例子也就能理解了,從而知其然也知其所以然。
作用域鏈是ECMAScript-262說明文檔中的概念,javascript引擎是按ECMAScript-262說明文檔去實現(xiàn)的,了解javascript引擎的工作原理有利于我們理解javascript的特性,但絕大多數(shù)js程序員不會去了解非常底層的技術(shù),所以閱讀ECMAScript-262說明文檔,我們可以有一個直觀的方式去模擬javascript引擎的工作原理。
本文將通過1999年的ECMAScript-262-3th第三版來說明作用域鏈的形成原理,將會介紹執(zhí)行環(huán)境,變量對象和活動對象,arguments對象,作用域鏈等幾個概念。2009年發(fā)布了ECMAScript-262-5th第五版,不同的是取消了變量對象和活動對象等概念,引入了詞法環(huán)境(Lexical Environments)、環(huán)境記錄(EnviromentRecord)等新的概念,所以兩個版本的概念不要混淆了。
重點來了!
1. 執(zhí)行環(huán)境(Execution Contexts)執(zhí)行環(huán)境(Execution Contexts)也被翻譯為執(zhí)行上下文,當解析器進入ECMAScript的可執(zhí)行代碼,解析器就進入一個執(zhí)行環(huán)境,活動的執(zhí)行環(huán)境組成一個邏輯上的棧,在這個邏輯棧頂部的執(zhí)行環(huán)境是當前運行的執(zhí)行環(huán)境。
注:ECMAScript中有三種可執(zhí)行代碼,Global、Function和Eval,全局環(huán)境即是Global可執(zhí)行代碼,函數(shù)即是Function可執(zhí)行代碼。邏輯棧是一種特殊的數(shù)據(jù)存儲格式,特點是‘先進后出,后進先出",添加數(shù)據(jù)會先壓入邏輯棧頂部,刪除數(shù)據(jù)必須先從頂部開始刪除。
變量對象(Variable Object)、活動對象(Activation Object)和Arguments對象(Arguments Object)
(上面這句話很重要哦)
每個執(zhí)行環(huán)境都有一個與之關(guān)聯(lián)的變量對象,當解析器進入執(zhí)行環(huán)境時,就會創(chuàng)建一個變量對象,變量對象保存著在當前執(zhí)行環(huán)境中聲明的變量和函數(shù)的引用。
變量對象是一個抽象的概念,在不同的執(zhí)行環(huán)境中,變量對象有不同的身份,在解析器進入任何執(zhí)行環(huán)境之前,就已經(jīng)創(chuàng)建了一個Global對象,當解析器進入全局執(zhí)行環(huán)境時,Global對象就充當變量對象,當解析器進入一個函數(shù)時,就會創(chuàng)建一個活動對象充當變量對象。
我的理解是:解析器在執(zhí)行代碼時,會遇到不同的執(zhí)行環(huán)境,此時,會創(chuàng)建一個變量對象,里面存放了環(huán)境內(nèi)的變量和對象(函數(shù))引用。
當執(zhí)行環(huán)境是變量,則會生成一個Global對象,此時變量對象就是Global對象
當執(zhí)行環(huán)境是函數(shù),則會生成一個活動對象(Activation Object)
2.解析器處理代碼時的兩個階段我們都知道javascript解析器是一段一段解析處理代碼的,為毛?這就要涉及解析器處理代碼時的兩個階段,解析代碼和執(zhí)行代碼。
當解析器進入執(zhí)行環(huán)境時,變量對象就會添加執(zhí)行環(huán)境中聲明的變量和函數(shù)作為它的屬性,這就意味著變量和函數(shù)在聲明之前已經(jīng)可用,變量值為undefined,這就是變量和函數(shù)聲明提升(Hoisting)的原因,與此同時作用域鏈和this確定,此過程為解析階段,俗稱預解析。接著解析器開始執(zhí)行代碼,為變量添加相應值的引用,得到執(zhí)行結(jié)果,此過程為執(zhí)行階段。
我們還是用栗子談吧
var a=123; var b="abc"; function c(){ alert("11"); }
記得之前那句話嗎?在解析器進入任何執(zhí)行環(huán)境之前,就已經(jīng)創(chuàng)建了一個Global對象,當解析器進入全局執(zhí)行環(huán)境時,Global對象就充當變量對象。一開始,JavaScript解析器就已經(jīng)生成了一個Global Object來充當變量對象,里面存放了全局環(huán)境里的變量,對象(函數(shù))等。就如上圖所示了,所以這也是為什么我們在函數(shù)內(nèi)部聲明變量時,聲名會提前,賦值undefined的原因了。到現(xiàn)在為止,執(zhí)行到這就是預解析的過程也叫解析代碼。
然后開始執(zhí)行賦值等操作,此過程就叫執(zhí)行過程。
再看這個:
function testFn(a){ var b="123"; function c(){ alert("abc"); } } testFn(10);
解析器進入函數(shù)執(zhí)行環(huán)境時,則會創(chuàng)建一個活動對象作為變量對象,活動對象還會創(chuàng)建一個Arguments對象,arguments對象是一個參數(shù)集合,用來保存參數(shù),這就是我們寫函數(shù)時可以使用arguments[0]等來使用參數(shù)的原因。
var a="123"; function testFn(b){ var c="abc"; function testFn2(){ var d="efg"; alert(a); } testFn2(); } testFn(10);
首先,在創(chuàng)建函數(shù)testFn時,作用域鏈內(nèi)([[scope]])就會先填入Global Object對象,圖片只例舉了全部變量中的一部分。
當解析器進入到testFn的執(zhí)行環(huán)境(執(zhí)行上下文)時,在將函數(shù)的活動對象添加到Global對象之前,注意是之前,形成一個作用域鏈。
然后,解釋器進入testFn2函數(shù)的執(zhí)行環(huán)境,同樣的,首先填入父級的作用域鏈,就是testFn的[[scope]]],包括了Global對象、testFn活動對象。之后再把testFn2的活動對象填入到作用域鏈最頂部,這就是testFn2的作用域鏈了。
testFn2調(diào)用變量a時,首先在當前的testFn2活動對象中查找,如果沒有找到就順著作用域鏈向上,在testFn活動對象中查找變量a,如果沒有找到再順著作用域鏈向上查找,直到在最后Global對象中找到為止,否則報錯。所以函數(shù)內(nèi)部可以調(diào)用外部環(huán)境的變量,外部環(huán)境不能調(diào)用函數(shù)內(nèi)部的變量,這就是作用域特性的原理。大概總結(jié)一下:
執(zhí)行環(huán)境:(Execution Contexts)也被翻譯為執(zhí)行上下文,當解析器進入ECMAScript的可執(zhí)行代碼,解析器就進入一個執(zhí)行環(huán)境,活動的執(zhí)行環(huán)境組成一個邏輯上的棧,在這個邏輯棧頂部的執(zhí)行環(huán)境是當前運行的執(zhí)行環(huán)境。
ECMAScript中有三種可執(zhí)行代碼,Global、Function和Eval,全局環(huán)境即是Global可執(zhí)行代碼,函數(shù)即是Function可執(zhí)行代碼。邏輯棧是一種特殊的數(shù)據(jù)存儲格式,特點是‘先進后出,后進先出",添加數(shù)據(jù)會先壓入邏輯棧頂部,刪除數(shù)據(jù)必須先從頂部開始刪除。
變量對象(Variable Object)、活動對象(Activation Object)和Arguments對象(Arguments Object)
每個執(zhí)行環(huán)境都有一個與之關(guān)聯(lián)的變量對象,當解析器進入執(zhí)行環(huán)境時,就會創(chuàng)建一個變量對象,變量對象保存著在當前執(zhí)行環(huán)境中聲明的變量和函數(shù)的引用。
變量對象是一個抽象的概念,在不同的執(zhí)行環(huán)境中,變量對象有不同的身份,在解析器進入任何執(zhí)行環(huán)境之前,就已經(jīng)創(chuàng)建了一個Global對象,當解析器進入全局執(zhí)行環(huán)境時,Global對象就充當變量對象,當解析器進入一個函數(shù)時,就會創(chuàng)建一個活動對象(Activation Object)充當變量對象。
大致過程: 1. 自動創(chuàng)建Global對象(當解析器進入全局執(zhí)行環(huán)境時,調(diào)用變量和函數(shù)時只在Global對象中查找。)
2. 解釋器進入執(zhí)行環(huán)境(執(zhí)行上下文)(也可理解為執(zhí)行函數(shù)時等等。)
3.生成變量對象(每個執(zhí)行環(huán)境都有一個與之關(guān)聯(lián)的變量對象,當解析器進入執(zhí)行環(huán)境時,就會創(chuàng)建一個變量對象,變量對象保存著在當前執(zhí)行環(huán)境中聲明的變量和函數(shù)的引用。)
(變量對象是一個抽象的概念,在不同的執(zhí)行環(huán)境中,變量對象有不同的身身份。)
4. 創(chuàng)建作用域鏈(執(zhí)行過程中的預解析、執(zhí)行階段)(每個執(zhí)行環(huán)境都有一個與之關(guān)聯(lián)的作用域鏈,當解析器進入執(zhí)行環(huán)境時被定義,作用域鏈是一個對象列表,用來檢索各個變量對象中的變量和函數(shù),這樣可以保證執(zhí)行環(huán)境有權(quán)訪問哪些變量和函數(shù))
(解析階段:當解析器進入執(zhí)行環(huán)境時,變量對象就會添加執(zhí)行環(huán)境中聲明的變量和函數(shù)作為它的屬性,這就意味著變量和函數(shù)在聲明之前已經(jīng)可用,變量值為undefined,這就是變量和函數(shù)聲明提升(Hoisting)的原因,與此同時作用域鏈和this確定,此過程為解析階段,俗稱預解析。
執(zhí)行階段:接著解析器開始執(zhí)行代碼,為變量添加相應值的引用,得到執(zhí)行結(jié)果,此過程為執(zhí)行階段。)
這里也就是說,變量對象先于作用域鏈創(chuàng)建前就以生成完畢?
5.按優(yōu)先級填入Global對象、活動對象等 6. 整個作用域鏈創(chuàng)建完成我們回到最初的題目,最后的例3中,調(diào)用fn時,并沒有傳參,所以fn函數(shù)的活動對象中沒有相關(guān)的鍵值(注意只是沒有值,但存在這個屬性),第一個alert彈出undefined,之后為其賦值,這時fn函數(shù)的活動對象中的scope就有值了,之后alert調(diào)用,搜索時自然先從優(yōu)先級最高的fn活動對象中尋找,然后彈出"local"。
而函數(shù)外的alert,依舊只有Global對象,其中的值未曾改變,所以彈出"global"
// undefined local global
了解作用域和作用域鏈都更好的幫助了解閉包噢。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/83310.html
摘要:至此作用域鏈創(chuàng)建完畢。好了,通過深入理解作用域鏈,我們能跟好的理解的運行機制和閉包的原理。 前言 理解javascript中的作用域和作用域鏈對我們理解js這們語言。這次想深入的聊下關(guān)于js執(zhí)行的內(nèi)部機制,主要討論下,作用域,作用域鏈,閉包的概念。為了更好的理解這些東西,我模擬了當一個函數(shù)執(zhí)行時,js引擎做了哪些事情--那些我們看不見的動作。 關(guān)鍵詞: 執(zhí)行環(huán)境 作用域 作用域鏈 變...
摘要:開篇作用域是每種計算機語言最重要的基礎(chǔ)之一,因此要想深入的學習作用域和作用域鏈就是個繞不開的話題。這樣由多個執(zhí)行上下文的變量對象構(gòu)成的鏈表就叫做作用域鏈。這時候執(zhí)行上下文的作用域鏈,我們命名為至此,作用域鏈創(chuàng)建完畢。 開篇 作用域是每種計算機語言最重要的基礎(chǔ)之一,因此要想深入的學習JavaScript,作用域和作用域鏈就是個繞不開的話題。 在《深入學習js之—-執(zhí)行上下文棧》中我們提到...
摘要:全局執(zhí)行環(huán)境的變量對象始終是作用域鏈中的最后一個變量對象。綜上,每個函數(shù)對應一個執(zhí)行環(huán)境,每個執(zhí)行環(huán)境對應一個變量對象,而多個變量對象構(gòu)成了作用域鏈,如果當前執(zhí)行環(huán)境是函數(shù),那么其活動對象在作用域鏈的前端。 1.幾個概念 先說幾個概念:函數(shù)、執(zhí)行環(huán)境、變量對象、作用域鏈、活動對象。這幾個東東之間有什么關(guān)系呢,往下看~ 函數(shù) 函數(shù)大家都知道,我想說的是,js中,在函數(shù)內(nèi)部有兩個特殊...
摘要:前言這段時間一直在消化作用域鏈和閉包的相關(guān)知識。而作用域鏈則是這套規(guī)則這套規(guī)則的具體運行。是變量對象的縮寫那這樣放有什么好處呢我們知道作用域鏈保證了當前執(zhí)行環(huán)境對符合訪問權(quán)限的變量和函數(shù)的有序訪問。 前言:這段時間一直在消化作用域鏈和閉包的相關(guān)知識。之前看《JS高程》和一些技術(shù)博客,對于這些概念的論述多多少少不太清楚或者不太完整,包括一些大神的技術(shù)文章。這也給我的學習上造成了一些困惑,...
摘要:每一個執(zhí)行上下文可以訪問的對象包括自身的作用域和父執(zhí)行上下文的作用域和父父執(zhí)行上下文作用域直到全局作用域,這就產(chǎn)生了作用域鏈。語句結(jié)束后,作用域鏈恢復正常。 0、自己理解 代碼執(zhí)行或函數(shù)調(diào)用生成執(zhí)行上下文(只有當前執(zhí)行上下文有執(zhí)行權(quán)),該執(zhí)行上下文內(nèi)只能訪問當前執(zhí)行上下文的變量、函數(shù)和上一級執(zhí)行上下文中的變量、函數(shù),激活下一個執(zhí)行上下文的時候執(zhí)行權(quán)轉(zhuǎn)移到新的執(zhí)行上下文,形成執(zhí)行上下文棧...
摘要:同時構(gòu)造函數(shù)內(nèi)部的被指定為。這時的作用域鏈是由的活動對象和全局對象組成的。在被調(diào)用時,它自身的活動對象被創(chuàng)建,然后添加到了中存儲著的作用域鏈的最前方。當函數(shù)執(zhí)行完畢時活動對象會被從該作用域鏈上刪除。參考自運算符作用域原理譯函數(shù)的作用域鏈 new的運行機制 當代碼new Animal(cat)執(zhí)行時: var obj=Object.create(Animal.prototype); 傳...
閱讀 3785·2023-04-26 02:07
閱讀 3671·2021-10-27 14:14
閱讀 2859·2021-10-14 09:49
閱讀 1624·2019-08-30 15:43
閱讀 2611·2019-08-29 18:33
閱讀 2369·2019-08-29 17:01
閱讀 915·2019-08-29 15:11
閱讀 582·2019-08-29 11:06