【深刻理解javascript】閉包

一、做用域

javascript沒有塊級做用域」。所謂「塊」,就是大括號「{}」中間的語句。例如if語句:javascript

再好比for語句:html

因此,咱們在編寫代碼的時候,不要在「塊」裏面聲明變量,要在代碼的一開始就聲明好了。以免發生歧義。如:前端

在聲明變量時,全局代碼要在代碼前端聲明,函數中要在函數體一開始就聲明好。除了這兩個地方,其餘地方都不要出現變量聲明。java

javascript除了全局做用域以外,只有函數能夠建立做用域閉包

做用域最大的用處就是隔離變量,不一樣做用域下同名變量不會有衝突。函數

二、做用域和執行上下文

做用域只是一個「地盤」,一個抽象的概念,其中沒有變量。要經過做用域對應的執行上下文環境來獲取變量的值。同一個做用域下,不一樣的調用會產生不一樣的執行上下文環境,繼而產生不一樣的變量的值。因此,做用域中變量的值是在執行過程當中產生的肯定的,而做用域倒是在函數建立時就肯定了spa

因此,若是要查找一個做用域下某個變量的值,就須要找到這個做用域對應的執行上下文環境,再在其中尋找變量的值。htm

三、從【自由變量】到【做用域鏈】

在A做用域中使用的變量x,卻沒有在A做用域中聲明(即在其餘做用域中聲明的),對於A做用域來講,x一個自由變量。以下圖blog

在調用函數時,函數中的變量取值要到建立這個函數的那個做用域中取值——建立」,而不是「調用」,切記切記!!!ip

  • 總結一下取自由變量時的這個「做用域鏈」過程:(假設a是自由量)(重點記住)

第一步,如今當前做用域查找a,若是有則獲取並結束。若是沒有則繼續;

第二步,若是當前做用域是全局做用域,則證實a未定義,結束;不然繼續;

第三步,(不是全局做用域,那就是函數做用域)將建立該函數的做用域做爲當前做用域;

第四步,跳轉到第一步。

例如:在fn函數中,取自由變量x的值時,要到哪一個做用域中取?——要到建立fn函數的那個做用域中取——不管fn函數將在哪裏調用

四、真正的主角登場了:閉包

閉包的兩種應用場景:函數做爲返回值;函數做爲參數傳遞!!!!!!!!

第一,函數做爲返回值

如上代碼,bar函數做爲返回值,賦值給f1變量。執行f1(15)時,用到了fn做用域下的max變量的值。至於如何跨做用域取值,能夠參考上一章。

第二,函數做爲參數被傳遞

如上代碼中,fn函數做爲一個參數被傳遞進入另外一個函數,賦值給f參數。執行f(15)時,max變量的取值是10,而不是100(由於fn建立做用域是全局做用域,而不是調用的匿名執行函數內)。

  • 帶閉包的做用域和執行上下文的場景是:(最重要,必定要看明白)

第一步,代碼執行前生成全局上下文環境,並在執行時對其中的變量進行賦值。此時全局上下文環境是活動狀態。

第二步,執行第17行代碼時,調用fn(),產生fn()執行上下文環境,壓棧,並設置爲活動狀態。

第三步,執行完第17行,fn()調用完成。按理說應該銷燬掉fn()的執行上下文環境,可是這裏不能這麼作。注意,重點來了:由於執行fn()時,返回的是一個函數。函數的特別之處在於能夠建立一個獨立的做用域。而正巧合的是,返回的這個函數體中,還有一個自由變量max要引用fn做用域下的fn()上下文環境中的max。所以,這個max不能被銷燬,銷燬了以後bar函數中的max就找不到值了。

所以,這裏的fn()上下文環境不能被銷燬,還依然存在與執行上下文棧中。

——即,執行到第18行時,全局上下文環境將變爲活動狀態,可是fn()上下文環境依然會在執行上下文棧中。另外,執行完第18行,全局上下文環境中的max被賦值爲100。以下圖:

 

第四步,執行到第20行,執行f1(15),即執行bar(15),建立bar(15)上下文環境,並將其設置爲活動狀態。

執行bar(15)時,max是自由變量,須要向建立bar函數的做用域中查找,找到了max的值爲10。這個過程在做用域鏈一節已經講過。

這裏的重點就在於,建立bar函數是在執行fn()時建立的。fn()早就執行結束了,可是fn()執行上下文環境還存在與棧中,所以bar(15)時,max能夠查找到。若是fn()上下文環境銷燬了,那麼max就找不到了。

使用閉包會增長內容開銷,如今很明顯了吧!

第五步,執行完20行就是上下文環境的銷燬過程

相關文章
相關標籤/搜索