成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

結(jié)合作用域,執(zhí)行上下文圖解閉包

msup / 2600人閱讀

摘要:作用域鏈所謂作用域鏈,是由當前環(huán)境與上層環(huán)境的一系列變量對象組成,它保證當前執(zhí)行環(huán)境對符合訪問權(quán)限的變量和函數(shù)的有序訪問。當我們在執(zhí)行函數(shù)的時候,需要的變量,在自己的作用域內(nèi)找不到,便會順著作用域鏈往上找,直到找到全局作用域。

一 作用域相關(guān)
? ? ? 作用域是一套規(guī)則,用來管理引擎如何查找變量。在es5之前,js只有全局作用域及函數(shù)作用域。es6引入了塊級作用域。但是這個塊級別作用域需要注意的是不是{}的作用域,而是let,const關(guān)鍵字的塊作用域。

1作用域
1.1 全局作用域
? ? ? 在全局環(huán)境下定義的變量,是掛載在window下的。如下代碼所示:

1.2 函數(shù)作用域

? ? ? 在函數(shù)內(nèi)定義的變量,值在函數(shù)內(nèi)部才生效,在函數(shù)外引用會報RefrenceError的錯誤

? ? ? 注意區(qū)分RefrenceError及TypeError。RefrenceError是在作用域內(nèi)找不到,而TypeError則是類型錯誤。如果只是定義了變量a 直接調(diào)用便會報TypeError的錯誤。

1.3 塊作用域

? ? ? es新增的關(guān)鍵字let,const是作用在塊級作用域。但是在js內(nèi){}形成的塊,是不具有作用域的概念的。如下所示,雖然for循環(huán)有一個{}包裹的塊,但是在塊外面還是可以訪問i的。

2 作用域鏈

? ? ? 所謂作用域鏈,是由當前環(huán)境與上層環(huán)境的一系列變量對象組成,它保證當前執(zhí)行環(huán)境對符合訪問權(quán)限的變量和函數(shù)的有序訪問。而作用域的最大的用處就是隔離變量,不同作用域下同名變量不會有沖突。

? ? ? 如上圖所示,會形成一個inner作用域到outer作用域到全局作用域的作用域鏈。當我們在執(zhí)行inner函數(shù)的時候,需要outName的變量,在自己的作用域內(nèi)找不到,便會順著作用域鏈往上找,直到找到全局作用域。在這個例子中,往上查找到outer作用域的時候便找到了。

? ? ? 簡單測試1:如下圖所示的代碼,大家覺得會輸出什么呢?

? ? ? 雖然fn的調(diào)用是在show內(nèi)調(diào)用的,但是因為fn所在的作用域是全局作用域,它的x的值會順著作用域鏈去全局作用域中啊,即x會輸出10。這里需要注意的一點是,變量的確定是在函數(shù)定義時候確定的,而不是函數(shù)運行時。

二 執(zhí)行上下文相關(guān)

? ? ? 函數(shù)每次被調(diào)用時,都會產(chǎn)生一個新的執(zhí)行上下文環(huán)境。全局上下文是存在棧中的。而處于棧頂?shù)娜稚舷挛囊坏﹫?zhí)行完就會自動出棧。如下圖所示的代碼。

? ? ? 首先是全局上下文入棧,然后開始執(zhí)行可執(zhí)行代碼。遇到outer(),激活outer()的上下文;

? ? ? 第二步,outer的上下文入棧。開始執(zhí)行outer內(nèi)的可執(zhí)行代碼,直到遇到inner()。激活inner()的上下文;

? ? ? 第三步,inner的上下文入棧。開始執(zhí)行inner內(nèi)的可執(zhí)行代碼。執(zhí)行完畢之后inner出棧。

? ? ? 第四步,inner的上下文出棧。outer內(nèi)繼續(xù)執(zhí)行可執(zhí)行代碼。如果一直沒有其他的執(zhí)行上下文,執(zhí)行完畢即可出棧;

? ? ? 第五步,outer的上下文出棧。

? ? ? ps:全局上下文只有瀏覽器關(guān)閉的時候才會出棧。

? ? ? 那我們已經(jīng)直到了全局上下文的宏觀入棧出棧的概念。具體的全局上下文包括哪些內(nèi)容,具體做了什么操作呢?

? ? ? 其實,執(zhí)行上下文分為準備階段和執(zhí)行階段。

? ? ? 1.在執(zhí)行上下文的準備階段,會有以下步驟:

? ? ? ? ? ? 1.1 創(chuàng)建變量對象:初始化arguments,函數(shù)聲明提升,變量聲明提升等

? ? ? ? ? ? 1.3 建立作用域鏈

? ? ?2.而在執(zhí)行上下文的執(zhí)行階段,會有以下步驟:

? ? ? ? ? ? 2.1 變量賦值

? ? ? ? ? ? 2.2 函數(shù)引用

? ? ? ? ? ? 2.3 確定this指向

? ? ? ? ? ? 2.4 執(zhí)行代碼

? ? ? 而在變量對象的創(chuàng)建過程,會經(jīng)歷以下的步驟。

? ? ? ? ? ? 1.創(chuàng)建arguments對象。也就是當前上下文中的參數(shù);

? ? ? ? ? ? 2.檢查當前上下文的函數(shù)聲明,即用function關(guān)鍵字聲明的函數(shù);

? ? ? ? ? ? 3.檢查當前上下文的變量聲明,即變量,屬性值為undefined。

? ? ? 而這個創(chuàng)建過程最重要的概念就是提升:

? ? ? 而如下圖所示的代碼執(zhí)行,變量對象的變化過程是怎樣的呢?

? ? ? 那函數(shù)內(nèi)的三個console分別會輸出什么呢?
? ? ? 因為在變量對象的創(chuàng)建過程中,是arguments=>函數(shù)聲明=>變量聲明的過程。在第一個console之前function foo()已經(jīng)被提升,因此第一次輸出的該函數(shù),而第二個console之前bar被提升,并賦值為undefined,因此第二次輸出的是undefined。而第三個console之前foo被重新賦值,因此第三個console是"hello"。

? ? ? 總結(jié)起來,變量對象和活動對象其實是同一個對象,他們只是在執(zhí)行上下文的不同階段的狀態(tài)而已。

? ? ? 下面的截圖即是兩個階段的變化。其實變量對象和活動對象是同一個對象,他們只是執(zhí)行上下文在不同階段的不同表現(xiàn)形式。在執(zhí)行階段變量對象V0會變成活動對象A0。內(nèi)部的一些引用也會發(fā)生變化。

? ? ? 而如下圖所示的代碼執(zhí)行,分別會輸出什么呢?

? ? ? 首先,第一段代碼。函數(shù)聲明首先會被提升第一個console輸出hello world。但是后面的hello會被覆蓋,第二個console輸出hello

? ? ? 第二段代碼。函數(shù)聲明首先會被提升,但是緊接著會被變量賦值覆蓋。因此,兩個console輸出hello。
總結(jié)起來,全局上下文的整個過程即下圖所示

? ? ? 那結(jié)合作用域即全局上下文呢,我們一開始的代碼代碼具體的圖解就是下面這張圖了。

三 閉包相關(guān)
1 閉包分析

? ? ? 此時,當我們修改inner函數(shù),返回上級作用域的outerName屬性時,閉包就產(chǎn)生了。

? ? ? 這里為什么會產(chǎn)生閉包呢?具體可以參考下方的圖示。
? ? ? 前面的全局入棧和outer函數(shù)入棧還是跟原來一樣,但是當我們的outer函數(shù)入棧執(zhí)行完畢準備出棧,準備被回收的時候,由于outName還被inner的作用域引用,不能被回收,產(chǎn)生了閉包。

? ? ? 即所謂的閉包就是通過函數(shù)調(diào)用,外部持有函數(shù)的句柄,讓函數(shù)的空間不能消失。產(chǎn)生的這塊獨體的空間永遠存在,這塊內(nèi)存對外也是封閉的。所以就叫閉包。

2 常見問題分析

? ? ? 相信大家在面試的時候會經(jīng)常問到這樣的面試題。下面這段代碼輸入的是什么呢?

? ? ? 這里輸出的是5個6。需要解釋這個問題呢,要涉及到j(luò)s的的執(zhí)行環(huán)境及作用域鏈了。

? ? ? js的執(zhí)行環(huán)境:JS是單線程環(huán)境,即代碼的執(zhí)行是從上到下,依次執(zhí)行。這樣的執(zhí)行稱為同步執(zhí)行。因為種種不要浪費和節(jié)約的原因。JS中引進了異步的機制。這塊具體的執(zhí)行邏輯可以參考https://segmentfault.com/a/11...。在這里,for循環(huán)是同步代碼,會先從上到下執(zhí)行。而setTimeout中的是異步代碼會將其插入到任務(wù)隊列當中等待。因此在setTimeout執(zhí)行的時候,for循環(huán)已經(jīng)執(zhí)行完成,i已經(jīng)變成6。作用域鏈。當setTimeout執(zhí)行的時候,會向上去查找i的值。往上查找,即for所在的作用域,已經(jīng)是6了。因此6次setTimeout都會輸出6。

? ? ? 那可能面試官會繼續(xù)問,我們怎樣才能依次輸出1-5呢?這里就可以用到閉包來解決了。

? ? ? 我們將i作為參數(shù)傳遞,并且形成了一個新的立即執(zhí)行函數(shù)作用域。當setTimeout執(zhí)行的時候,去查找i。即在立即執(zhí)行函數(shù)作用域查找,此時的i我們可以根據(jù)上面一部分的分析,形成了閉包之后,它的內(nèi)存是不會消失的。因此這每次循環(huán)的時候都是當前i即1-5。

3 閉包的查看

? ? ? 其實,我們在chrome的控制臺是可以去查看閉包的。在瀏覽器斷點調(diào)試,可以去觀察下面兩幅圖的紅色圈區(qū)別。第二副圖可以看到closure,i值是1。依次執(zhí)行,可以看到i從1到5的變化。

?

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/104735.html

相關(guān)文章

  • 圖解JS閉包形成的原因

    摘要:閉包的出現(xiàn)正好結(jié)合了全局變量和局部變量的優(yōu)點。這就是閉包的一個使用場景保存現(xiàn)場。 前言 什么是閉包,其實閉包是可以重用一個對象,又保護對象不被篡改的一種機制。什么是重用一個對象又保護其不被篡改呢?請看下面的詳解。 作用域和作用域鏈 注意理解作用域和作用域鏈對理解閉包有非常大的幫助,所以我們先說一下作用域和作用域鏈 什么是作用域作用域表示的是一個變量的可用范圍、其實它是一個保存變量的對象...

    wind3110991 評論0 收藏0
  • 前端基礎(chǔ)進階(四):詳細圖解作用鏈與閉包

    摘要:之前一篇文章我們詳細說明了變量對象,而這里,我們將詳細說明作用域鏈。而的作用域鏈,則同時包含了這三個變量對象,所以的執(zhí)行上下文可如下表示。下圖展示了閉包的作用域鏈。其中為當前的函數(shù)調(diào)用棧,為當前正在被執(zhí)行的函數(shù)的作用域鏈,為當前的局部變量。 showImg(https://segmentfault.com/img/remote/1460000008329355);初學(xué)JavaScrip...

    aikin 評論0 收藏0
  • 【進階2-1期】深入淺出圖解作用鏈和閉包

    摘要:本期推薦文章從作用域鏈談閉包,由于微信不能訪問外鏈,點擊閱讀原文就可以啦。推薦理由這是一篇譯文,深入淺出圖解作用域鏈,一步步深入介紹閉包。作用域鏈的頂端是全局對象,在全局環(huán)境中定義的變量就會綁定到全局對象中。 (關(guān)注福利,關(guān)注本公眾號回復(fù)[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實戰(zhàn)、面試指導(dǎo)) 本周開始前端進階的第二期,本周的主題是作用域閉包,今天是第6天。 本...

    levius 評論0 收藏0
  • 【進階2-2期】JavaScript深入之從作用鏈理解閉包

    摘要:使用上一篇文章的例子來說明下自由變量進階期深入淺出圖解作用域鏈和閉包訪問外部的今天是今天是其中既不是參數(shù),也不是局部變量,所以是自由變量。 (關(guān)注福利,關(guān)注本公眾號回復(fù)[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實戰(zhàn)、面試指導(dǎo)) 本周正式開始前端進階的第二期,本周的主題是作用域閉包,今天是第7天。 本計劃一共28期,每期重點攻克一個面試重難點,如果你還不了解本進階計...

    simpleapples 評論0 收藏0
  • javascript系列--javascript深入淺出圖解作用鏈和閉包

    摘要:變量對象也是有父作用域的。作用域鏈的頂端是全局對象。當函數(shù)被調(diào)用的時候,作用域鏈就會包含多個作用域?qū)ο?。當函?shù)要訪問時,沒有找到,于是沿著作用域鏈向上查找,在的作用域找到了對應(yīng)的標示符,就會修改的值。 一、概要 對于閉包的定義(紅寶書P178):閉包就是指有權(quán)訪問另外一個函數(shù)的作用域中的變量的函數(shù)。 關(guān)鍵點: 1、閉包是一個函數(shù) 2、能夠訪問另外一個函數(shù)作用域中的變量 二、閉包特性 對...

    Jensen 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<