摘要:那其實閉包的原因就是外層函數(shù)的作用域?qū)ο鬅o法釋放其實就是一個函數(shù)調(diào)用會生成的臨時作用域圖中可看出其實就是在中的匿名函數(shù),所以他的就指向留下的作用域。
引言
網(wǎng)絡(luò)上關(guān)于作用域及閉包的文章很多,自己對于純理論知識并不能很快的理解,但自己對于圖畫有很強的記憶和理解能力,因此決定將此知識點以圖畫的知識表現(xiàn)出來,加深自身理解的同時如果能幫到正在學習的童鞋就再好不過了
下面我以函數(shù)的整個生命周期來訴說此部分知識
函數(shù)生命周期先寫一下示例代碼
var a = 10; function func(a) { var a = 20; a++; console.log(a); } func(); console.log(a);開始執(zhí)行程序前
先創(chuàng)建 ECS,ECS 其實就是專門保存正在調(diào)用的函數(shù)的執(zhí)行環(huán)境的數(shù)組,也可以說對象,其實關(guān)聯(lián)數(shù)組也就相當于對象。
然后在 ECS 中添加瀏覽器主程序的執(zhí)行環(huán)境 main
創(chuàng)建全局作用域?qū)ο?window
main 執(zhí)行環(huán)境引用 window
定義函數(shù)時原始類型的全局變量會直接存入 window 環(huán)境當中,因為函數(shù)是引用類型,所以首先用函數(shù)名聲明全局變量
然后創(chuàng)建函數(shù)對象,封裝函數(shù)定義
函數(shù)對象的 scope 屬性,指回函數(shù)創(chuàng)建時的作用域,意思是,函數(shù)執(zhí)行時如果函數(shù)本身提供的變量不能讓函數(shù)執(zhí)行完全,那它便會去回它創(chuàng)建時的那個作用域去尋找變量。
函數(shù)名后面存入指向函數(shù)對象的地址
引用類型在其中只能存儲地址,這個在此筆記談?wù)勚祩鬟f中有詳細說明
函數(shù)調(diào)用時向 ECS 中壓入本次函數(shù)調(diào)用的執(zhí)行環(huán)境元素
創(chuàng)建本次函數(shù)調(diào)用時使用的函數(shù)作用域?qū)ο螅ˋO),也就是臨時作用域
在 AO 中創(chuàng)建儲存所有的局部變量,包括形參變量和函數(shù)內(nèi)用 var 聲明的變量
設(shè)置 AO 的 parent 屬性和引用函數(shù)的 scope 屬性指向父級作用域?qū)ο?/p>
函數(shù)的執(zhí)行環(huán)境引用 AO
順著那個箭頭,先在 AO 中找變量,也就是局部變量,如果 AO 中沒有,再順著箭頭去父級作用域中找
函數(shù)調(diào)用后函數(shù)的執(zhí)行環(huán)境出棧,AO 釋放,AO 中的局部變量一同被釋放掉。
我們得知整個結(jié)果之后,自然而然那兩個 console 的結(jié)果也顯然意見。
閉包前面我們提到過,全局變量是可重用但是污染全局,局部變量不會污染全局但是不可重用。
我自己認為閉包就是重用變量又保護變量不被污染的機制,就是為了解決這一情況而生的。
特點包裹受保護的變量和操作變量的內(nèi)層函數(shù)的外層函數(shù)
外層函數(shù)要返回內(nèi)層函數(shù)的對象
return function(){..}
直接給全局變量賦值一個內(nèi)部 function
將內(nèi)部函數(shù)保存在一個對象的屬性或數(shù)組元素中 return [function function function] 或 return {fun:function(){...}}
調(diào)用外層函數(shù),用外部變量接住返回的內(nèi)層函數(shù)對象,形成閉包。
原理先貼出示例代碼
function outer() { var num = 1; return function() { console.log(num++); }; } var getNum = outer(); getNum(); getNum(); num = 1; getNum();
下面我把閉包形成的原理用畫圖工具畫出來
window 中存入 outer 名并指向 outer 函數(shù)對象,getNum 因為聲明提前也先將變量名存在 window 中。
getNum = outer() 其實包含 outer 的創(chuàng)建和 getNum 的賦值。
上面的圖畫的是 outer 函數(shù)進行到 var num = 1; ,前面都有說過,不過多重復(fù)。
創(chuàng)建了匿名函數(shù),getNum 指向了匿名函數(shù)對象,匿名對象的 scope 指向它的父級作用域,也就是 outer 的作用域,那這樣就形成了圖中的三角關(guān)系,此時 outer 執(zhí)行完畢,離開 ECS 執(zhí)行環(huán)境,outer 的 AO 本也應(yīng)該隨著離開,但是因為這強大的三角關(guān)系,強行拉住不讓其釋放,也就形成了所謂的閉包。
那其實閉包的原因就是:外層函數(shù)的作用域?qū)ο鬅o法釋放
getNum=outer()getNum 其實就是一個函數(shù)
調(diào)用getNum(),會生成 getNum 的臨時作用域,圖中可看出,getNum 其實就是在 outer 中的匿名函數(shù),所以他的 parent 就指向 outer 留下的作用域。當他執(zhí)行 console.log(num++) 的時候,在他的作用域中沒有 num 變量他就會順著作用域鏈去尋找,最終在 outer 中的作用域中找到 num 并對其進行自加操作。所以當下次調(diào)用 getNum 的時候 num 會從 2 開始,不會是一開始的 1。
num 不是全局變量,還實現(xiàn)了 num 變量的重復(fù)調(diào)用。就達到了閉包的目的。
設(shè)置 num = 1 只是在 window 對象上添加存儲 num 的值,當下次調(diào)用 getNum 的時候 js 引擎還會從 getNum 作用域開始順著作用域鏈尋找 num,在 outerAO 就會尋找到 num,所以根本不會影響到 window 中的 num,也不會受其影響。因此此段代碼輸出的結(jié)果為 1 2 3。
缺點當然閉包也有其缺點
比普通函數(shù)占用更多內(nèi)存,因為外層函數(shù)的作用域?qū)ο螅ˋO)始終存在
容易造成內(nèi)存泄漏
解決辦法將引用內(nèi)存函數(shù)對象的外部變量重置為 null
getNum = null;
getNum 指向 outer 函數(shù)對象的那根線就會斷掉,三角關(guān)系破裂,那函數(shù)對象和 outerAO 也會相繼被銷毀。
覺得文章不錯的話還請各位大佬給個 star 鼓勵一下 gayhub
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/94802.html
摘要:變量對象也是有父作用域的。作用域鏈的頂端是全局對象。當函數(shù)被調(diào)用的時候,作用域鏈就會包含多個作用域?qū)ο蟆.敽瘮?shù)要訪問時,沒有找到,于是沿著作用域鏈向上查找,在的作用域找到了對應(yīng)的標示符,就會修改的值。 一、概要 對于閉包的定義(紅寶書P178):閉包就是指有權(quán)訪問另外一個函數(shù)的作用域中的變量的函數(shù)。 關(guān)鍵點: 1、閉包是一個函數(shù) 2、能夠訪問另外一個函數(shù)作用域中的變量 二、閉包特性 對...
摘要:作用域鏈所謂作用域鏈,是由當前環(huán)境與上層環(huán)境的一系列變量對象組成,它保證當前執(zhí)行環(huán)境對符合訪問權(quán)限的變量和函數(shù)的有序訪問。當我們在執(zhí)行函數(shù)的時候,需要的變量,在自己的作用域內(nèi)找不到,便會順著作用域鏈往上找,直到找到全局作用域。 一 作用域相關(guān)? ? ? 作用域是一套規(guī)則,用來管理引擎如何查找變量。在es5之前,js只有全局作用域及函數(shù)作用域。es6引入了塊級作用域。但是這個塊級別作用域...
摘要:將作用域賦值給變量這里的作用域是,而不是將作用域賦值給一個變量閉包返回瀏覽器中內(nèi)存泄漏問題大家都知道,閉包會使變量駐留在內(nèi)存中,這也就導(dǎo)致了內(nèi)存泄漏。 上一章我們講了匿名函數(shù)和閉包,這次我們來談?wù)勯]包中作用域this的問題。 大家都知道,this對象是在運行時基于函數(shù)的執(zhí)行環(huán)境綁定的,如果this在全局就是[object window],如果在對象內(nèi)部就是指向這個對象,而閉包卻是在運行...
摘要:注此讀書筆記只記錄本人原先不太理解的內(nèi)容經(jīng)過閱讀你不知道的后的理解。作用域及閉包基礎(chǔ),代碼運行的幕后工作者引擎及編譯器。 注:此讀書筆記只記錄本人原先不太理解的內(nèi)容經(jīng)過閱讀《你不知道的JS》后的理解。 作用域及閉包基礎(chǔ),JS代碼運行的幕后工作者:引擎及編譯器。引擎負責JS程序的編譯及執(zhí)行,編譯器負責詞法分析和代碼生成。那么作用域就像一個容器,引擎及編譯器都從這里提取東西。 ...
摘要:不是引用類型,無法輸出簡而言之,堆內(nèi)存存放引用值,棧內(nèi)存存放固定類型值。變量的查詢在變量的查詢中,訪問局部變量要比全局變量來得快,因此不需要向上搜索作用域鏈。 贊助我以寫出更好的文章,give me a cup of coffee? 2017最新最全前端面試題 基本類型值有:undefined,NUll,Boolean,Number和String,這些類型分別在內(nèi)存中占有固定的大小空...
閱讀 3171·2023-04-25 18:22
閱讀 2416·2021-11-17 09:33
閱讀 3343·2021-10-11 10:59
閱讀 3252·2021-09-22 15:50
閱讀 2831·2021-09-10 10:50
閱讀 870·2019-08-30 15:53
閱讀 460·2019-08-29 11:21
閱讀 2933·2019-08-26 13:58