理解閉包

知識小儲備

ECMAScript 的數據有兩種類型:基本類型值和引用類型值,基本類型指的是簡單的數據段,引用類型指的是可能由多個值構成的對象。

Undefined、Null、Boolean、Number 和 String 是值類型,其餘都是引用類型。前端

垃圾回收

咱們建立的原始類型、對象、函數等等,都會佔用內存。爲了防止溢出,咱們就須要對不用的數據進行刪除。這就是垃圾回收。segmentfault

可觸及(Reachability)

JavaScript 內存管理的關鍵概念是可觸及(Reachability)。
個人理解就是還處於被引用狀態。
將全局(不管是window仍是global)比做樹根,咱們建立的原始類型、對象、函數等等比做一個個枝杈。若是能夠從window不斷層的知道某個變量,那這個變量就是可觸及的,不可回收的。數組

舉個例子:閉包

// user has a reference to the object
let user = {
  name: "John"
};

圖片描述

箭頭表明的是對象引用。全局變量 "user" 引用了對象{name: "John"}(簡稱此對象爲 John)。John 的 "name" 屬性儲存的是一個原始值,因此無其餘引用。函數

若是覆蓋 user,對 John 的引用就丟失了:性能

user = null;

clipboard.png

如今 John 變得不可觸及,垃圾回收機制會將其刪除並釋放內存。spa

內存機制

js的內存空間分爲棧 (stack)、堆 (heap);其中棧存放變量,堆存放複雜對象。
借用一張圖直觀感覺一下
圖片描述3d

棧內存

只能存放基本數據類型的數據和對象類型的引用地址也叫哈希碼。裏面的數據後進先出。
對棧內數據進行復制修改時:
圖片描述code

堆內存

是用來存儲 「數組類型」 和「對象類」的數據。特色是存儲空間大。
對堆內數據進行復制修改時:
圖片描述對象

理解閉包

有了前面的鋪墊,咱們再來看看閉包是怎麼回事。仍是舉個例子:

//決策層開會決定生產新一代phone手機,就弄了個叫PhoneFactory的企劃案
let Proposal = function(){
    //新一代手機信息被封裝在企劃案中
    let  version = "XX", money = 10000
    return function (){
        //生產新的手機
        return {
            version: version,
            money: money
        }
    }
}
//執行者根據策劃案建成了一個工廠, 工廠方法每執行一次就產出一個手機
let Factory = new Proposal();
let phone1 = Factory();
console.log(phone1.version)

對於手機的version, money而言, 它是策劃書Proposal的內部變量,而Proposal的同級phone1應該是訪問不到。但實際上咱們仍是拿到了手機的版本和價格數據。這種反常的現象咱們就叫它Closure,中文名閉包。

緣由

咱們無論它爲何叫這個名字,先看看具體是什麼緣由產生的。
根據上面說的垃圾回收機制。函數Proposal在執行過以後(第16行)就沒有引用。那麼Proposal久應該被回收。裏面的全部內部變量也應該被回收了。
但實際上 Proposal返回了一個新的函數Factory。而這個Factory是要可以訪問到它生成時的同級以及父祖輩變量。而Proposal內部變量version, money就有了新的引用。於是阻止了被回收。就像Factory生成了一個新的泡泡把它能訪問到的做用域包裹了起來。這就是閉包造成的緣由了。

基於上面的js內存機制的知識,咱們能夠畫出下面這張圖:
圖片描述

圖簡陋了點。。。。
但也能夠看出,對於變量version,money而言。雖然他們自己在proposal的黃色做用域中。但也在fatory生成的時候也被包含在了fatory打的可訪問的做用域氣泡內。不只他們,甚至更外層的也都被包含在內。
在proposal這個泡泡破碎以後,只有當打的紅色泡泡也破了,這些變量纔會真的被回收。這也是爲何閉包用多了會影響性能的緣由。

引用:

前端基礎進階:詳細圖解 JavaScript 內存空間
垃圾回收

相關文章
相關標籤/搜索