丨作用域鏈和閉包代碼中出現(xiàn)相同的變量javascript引擎是如何選擇_第1頁
丨作用域鏈和閉包代碼中出現(xiàn)相同的變量javascript引擎是如何選擇_第2頁
丨作用域鏈和閉包代碼中出現(xiàn)相同的變量javascript引擎是如何選擇_第3頁
丨作用域鏈和閉包代碼中出現(xiàn)相同的變量javascript引擎是如何選擇_第4頁
丨作用域鏈和閉包代碼中出現(xiàn)相同的變量javascript引擎是如何選擇_第5頁
已閱讀5頁,還剩25頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

首先我們來看下面這段代碼代functionbar()3functionfoo()varmyName="7varmyName="極客時間你覺得這段代碼中的bar數(shù)和foo數(shù)打印出來的內(nèi)容是什么?這就要分析下這兩段代了。那么當這段代碼執(zhí)行到bar函數(shù)內(nèi)部時,其調(diào)用棧的狀態(tài)圖如下所示:執(zhí)行bar函數(shù)時的調(diào)用從圖中可以看出,全局執(zhí)行上下文和foo函數(shù)的執(zhí)行上下文中都包含變量myName,bar數(shù)里面myName值到底該選擇哪個呢也許你的第一反應是按照調(diào)用棧的順序來查找變量,查找方式如先查找棧頂是否存在myName量,但是這里沒有,所以接著往下查找foo數(shù)中的在foo函數(shù)中查找到了myName變量,這時候就使用foo函數(shù)中的myName如果按照這種方式來查找變量,那么最終執(zhí)行bar函數(shù)打印出來的結(jié)果就應該邦”。但實際情況并非如此,如果你試著執(zhí)行上述代碼,你會發(fā)現(xiàn)打印出來的結(jié)果是“極客關(guān)于作用域鏈,很多人會感覺費解,但如果你理解了調(diào)用棧、執(zhí)行上下文、詞法環(huán)境、變量環(huán)境等概念,那么你理解起來作用域鏈也會很容易。所以很是建議你結(jié)合前幾篇文章將上面那幾個概念學習透徹。其實在每個執(zhí)行上下文的變量環(huán)境中,都包含了一個外部用來指向外部的執(zhí)行上下當一段代碼使用了一個變量時,JavaScript引擎首先會在“當前的執(zhí)行上下文”中查找該比如上面那段代碼在查找myName變量時,如果在當前的變量環(huán)境中沒有查找到,那么JavaScript引擎會繼續(xù)在outer所指向的執(zhí)行上下文中查找。為了直觀理解,你可以看下帶有外部的調(diào)用棧示意從圖中可以看出,bar函數(shù)和foo函數(shù)的outer都是指向全局上下文的,這也就意味著如果在bar函數(shù)或者foo函數(shù)中使用了外部變量,那么JavaScript引擎會去全局執(zhí)行上下文現(xiàn)在你知道變量是通過作用域鏈來查找的了,不過還有一個疑問沒有解開,foo數(shù)調(diào)用的bar函數(shù),那為什么bar函數(shù)的外部是全局執(zhí)行上下文,而不是foo函數(shù)的執(zhí)行上下要回答這個問題,你還需要知道什么是詞法作用域。這是因為在JavaScript行過程中,這么講可能不太好理解,你可以看下面這詞法作用從圖中可以看出,詞法作用域就是根據(jù)代碼的位置來決定的,其中main函數(shù)包含了bar函數(shù),bar數(shù)中包含了foo數(shù),因為JavaScript用域鏈是由詞法作用域決定的,所以整個詞法作用域鏈的順序是:foo函數(shù)作用域—>bar函數(shù)作用域—>main函數(shù)作用域—>全局作用域了解了詞法作用域以及JavaScript中的作用域鏈,我們再回過頭來看看上面的那個問題:在開頭那段代碼中,foo函數(shù)調(diào)用了bar函數(shù),那為什么bar函數(shù)的外部是全局執(zhí)行上下文,而不是foo函數(shù)的執(zhí)行上下文?這是因為根據(jù)詞法作用域,foo和bar的上級作用域都是全局作用域,所以如果foo或者bar數(shù)使用了一個它們沒有定義的變量,那么它們會到全局作用域去查找。也就是說,詞變量,這時JavaScript引擎就需要按照作用域鏈在其他作用域中查找該變量,如果你不了我們還是先看下面這段代碼代1functionbar()2varmyName="極客世界3lettest1=4if(1)5letmyName="Chrome瀏覽"67}8functionfoo()varmyName="lettest={lettest= 16varmyName="極客時間letmyAge=lettest=你可以自己先分析下這段代碼的執(zhí)行流程,看看能否分析出來執(zhí)行結(jié)在上篇文章中我們已經(jīng)介紹過了,ES6是支持塊級作用域的,當執(zhí)行到代碼塊時,如果代碼塊中有l(wèi)et或者const的變量,那么變量就會存放到該函數(shù)的詞法環(huán)境中。對于上面這段代碼,當執(zhí)行到bar函數(shù)內(nèi)部的if語句塊時,其調(diào)用棧的情況如下圖所示:塊級作用域中是如何查找變量現(xiàn)在是執(zhí)行到bar函數(shù)的if語塊之內(nèi),需要打印出來變量test,那么就需要查找到test變量的值,其查找過程我已經(jīng)在上圖中使用序號1、2、3、4、5標記出來了。下面我就來解釋下這個過程。首先是在bar函數(shù)的執(zhí)行上下文中查找,但因為bar函數(shù)的執(zhí)行上下文中沒有定義test變量,所以根據(jù)詞法作用域的規(guī)則,下一步就在bar函數(shù)的外至于單個執(zhí)行上下文中如何查找變量,我在上一篇文章中已經(jīng)做了介紹,這里就不重別是在你不太熟悉JavaScript這門語言的時候,接觸閉包很可能會讓你產(chǎn)生一些挫敗感,最的是,JavaScript代碼中還總是充斥著大量的閉包代碼。但理解了變量環(huán)境、詞法環(huán)境和作用域鏈等概念,那接下來你再理解什么是JavaScript代functionfoo()varmyName="極客時間lettest1=consttest2=varinnerBar=return myName= return15varbar=bar.setName("首先我們看看當執(zhí)行到foo數(shù)內(nèi)部的returninnerBar這行代碼時調(diào)用棧的情況,你執(zhí)行到returnbar時候的調(diào)用從上面的代碼可以看出,innerBar是一個對象,包含了getName和setName的兩個方法(通常我們把對象內(nèi)部的函數(shù)稱為方法)。你可以看到,這兩個方法都是在foo函數(shù)內(nèi)部定義的,并且這兩個方法內(nèi)部都使用了myName和test1兩個變量。根據(jù)詞法作用域的規(guī)則,內(nèi)部函數(shù)getName和setName總是可以它們的外部函數(shù)foo中的變量,所以當innerBar對象返回給全局變量bar時,雖然foo函數(shù)已經(jīng)執(zhí)行結(jié)束,但是getName和setName函數(shù)依然可以使用foo函數(shù)中的變量myName和test1。所以當foo函數(shù)執(zhí)行完成之后,其整個調(diào)用棧的狀態(tài)如下圖所示:閉包的產(chǎn)生過從上圖可以看出,foo函數(shù)執(zhí)行完成之后,其執(zhí)行上下文從棧頂彈出了,但是由于返回的setName和getName方法中使用了foo函數(shù)內(nèi)部的變量myName和test1,所以這兩個變量依然保存在內(nèi)存中。這像極了setName和getName方法背的一個專屬背包,無論在哪里調(diào)用了setName和getName方法,它們都會背著這個foo函數(shù)的專屬背包。之所以是專屬背包,是因為除了setNamegetName數(shù)之外,其他任何地方都是無法該背包的,我們就可以把這個背包稱為foo函數(shù)的閉包。好了,現(xiàn)在我們終于可以給閉包一個正式的定義了。在JavaScript中,根據(jù)詞法作用域的規(guī)則,內(nèi)部函數(shù)總是可以其外部函數(shù)中的變量,當通過調(diào)用一個外部函數(shù)返回一內(nèi)部函數(shù)后,即使該外部函數(shù)已經(jīng)執(zhí)行結(jié)束了,但是內(nèi)部函數(shù)外部函數(shù)的變量依然保在內(nèi)存中,我們就把這些變量的集合稱為閉包。比如外部函數(shù)是foo,那么這些變量的集合就稱為foo函數(shù)的閉包。那這些閉包是如何使用的呢?當執(zhí)行到bar.setName方法中的myName=""這句代碼時,JavaScript擎會沿著“當前執(zhí)行上下文–>foo數(shù)閉包局執(zhí)行上下文”的順序來查找myName變量,你可以參考下面的調(diào)用棧狀態(tài)圖:執(zhí)行bar時調(diào)用棧從圖中可以看出,setName執(zhí)行上下文中沒有myName量,foo數(shù)的閉包中包含了變量myName,所以調(diào)用setName時,會修改foo閉包中的myName變量的值。同樣的流程,當調(diào)用bar.getName的時候,所的變量myName也是位于foo函數(shù)閉你也可以通過“開發(fā)者工具”來看看閉包的情況,打開Chrome的“開發(fā)者工具”,bar函數(shù)任意地方打上斷點,然后刷新頁面,可以看到如下內(nèi)容開發(fā)者工具中的閉包展從圖中可以看出來,當調(diào)用bar.getName的時候,右邊Scope項就體現(xiàn)出了作用域鏈的情況:Local就是當前的getName函數(shù)的作用域,Closure(foo)是指foo函數(shù)的閉包,最下面的Global就是指全局作用域,從“Local–>Closure(foo)–>Global”就是一個完整所以說,你以后也可以通過Scope來查看實際代碼作用域鏈的情況,這樣調(diào)試代碼也會比通常,如果閉包的函數(shù)是一個全局變量,那么閉包會一直存在直到頁面關(guān)閉;但如果這如果閉包的函數(shù)是個局部變量,等函數(shù)銷毀后,在下次JavaScript引擎執(zhí)行回收時,判斷閉包這塊內(nèi)容如果已經(jīng)不再被使用了,那么JavaScript引擎的回收器就會回收這塊內(nèi)存所以在使用閉包的時候,你要盡量注意一個原則:如果該閉包會一直使用,那么它可以作為全局變量而存在;但如果使用頻率不高,而且占用內(nèi)存又比較大的話,那就盡量讓它成為一關(guān)于閉包回收的問題本文只是做了個簡單的介紹,其實閉包是如何回收的還牽涉到 的回收機制,而關(guān)于回收,后續(xù)章節(jié)我會再為你做詳細介紹的好了,今天的內(nèi)容就講到這里,下面我們來回顧下今天的內(nèi)通過展開詞法作用域,我們介紹了JavaScript中的作用域鏈和閉包;通過詞法作用域,我們分析了在JavaScript的執(zhí)行過程中,作用域鏈是已經(jīng)注定好了,比如即使在foo函數(shù)中調(diào)用了bar函數(shù),你也無法在bar函數(shù)中直接使用foo函數(shù)中的變量信息。因此理解詞法作用域?qū)τ谀憷斫釰avaScript語言本身有著非常大幫助,比助于你理解下一篇文章中要介紹的this。另外,理解詞法作用域?qū)τ谀憷斫馄渌Z言也有很大的幫助,因為它們的邏輯都是一樣代1bar=23printName:function()45}6functionfoo()letmyName="極客時間return10letmyName="let_printName=在上面這段代碼中有三個地方定義了myName,分析這段代碼,你覺得這段代碼在執(zhí)行過覺得這篇文章對你有幫助的話,也歡迎把它給的朋友。 歸科技所有 不得售賣。頁面已增加防盜追蹤,將依法其上一 09|塊級作用域:var缺陷以及為什么要引入let和下一 11|this:從JavaScript執(zhí)行上下文的視角講清楚精選留言varbar{ printName:function(){}展1 6這道題其實是個障眼法,只需要確定好函數(shù)調(diào)用棧就可以很輕松的解答,調(diào)用了foo()后,展1 51.bar不是一個函數(shù),因此bar當中的printName其實是一個全局的函數(shù),bar當中的myNameprintName沒有聯(lián)系,如果要產(chǎn)生聯(lián)系,需要使用this關(guān)鍵字,表示這里的myName是對象的一個屬性,不然的話,printName會通…5首先兩個函數(shù)都會打印:拿到內(nèi)部函數(shù)的返回值,所以內(nèi)部函數(shù)不會被回收,這個私有作用域就是閉包.12sicp展1 3{function展function functiong(){returntrue;}}}foo(){ varg=function(){returntrue;}}}3 2展1展變量不會被回收,這塊內(nèi)容要到講v8GC那節(jié)來講了。1 1展11 1函數(shù)變量對函數(shù)

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論