iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 11
0
Modern Web

從技術文章深入學習 JavaScript系列 第 20

Day 20 [整理02] 淺析JavaScript閉包

  • 分享至 

  • xImage
  •  

先來看一下各個不同地方的閉包說法

  1. 你不知道的JavaScript: 當函數可以記住並訪問所在的詞法作用域時及形成閉包,即使不是在該函數的詞法作用域內
  2. JavaScript高级程序设计: 閉包是指有權訪問令一個函數作用域變量的函數
  3. MDN: 閉包(Closure)是函式以及該函式被宣告時所在的作用域環境(lexical environment)的組合。

我自己的理解

如果我們把函數作用域想像成一個書包(從外面是看不到裡面的內容的),那閉包就很像一個超一流扒手,可以偷窺到你書包的東西。

先來個情境

聲明一個背包函數

function package() {
  let pencilCase = {
    '原子筆': 2,
    '橡皮擦': 1,
    '水壺': 1
  }
  console.log(pencilCase); 
}

外界的人看起來會長怎樣(那整個黃黃的一塊講的艱澀一點就是package函數的詞法作用域)

https://ithelp.ithome.com.tw/upload/images/20201004/20124350OrN10FAOJF.png

假設今天有一個扒手函數可以調用到鉛筆盒那就形成閉包(如同先前定義的)

附註: 這裡我對package裡面改寫成這樣:

function package() {
  let pencilCase = {
    '原子筆': 2,
    '橡皮擦': 1,
    '水壺': 1
  }
  return {
    getPencilCase() {
      console.log(pencilCase);
    }
  }
}

之後扒手開扒

let pickpocket = package() // 通常函數執行完該作用域的東西就該銷毀了
pickpocket.getPencilCase()

https://ithelp.ithome.com.tw/upload/images/20201004/20124350LNYZnLPnAM.png

可以發現確實成功獲得書包裡面的鉛筆盒!!!!

其實這跟棧內存有關

擷取

https://juejin.im/post/6844904099771580423

棧內存銷毀:

  • 全局棧內存:關掉頁面的時候才會銷毀
  • 私有棧內存:
    • 1.一般情況下,函數只要執行完成,形成的私有棧內存就會被銷毀釋放掉(排除出現無限極遞歸、出現死循環的模式)
    • 2.但是一旦棧內存中的某個東西(一般都是堆地址)被私有作用域以外的事物給佔用了,則當前私有棧內存不能立即被釋放銷毀(特點:私有作用域中的私有變量等信息也保留下來了=>這種函數執行形成不能被釋放的私有棧內存,也叫做閉包)

從上面兩個可以得出函數作用域裡面確實不會被立即銷毀

到此我們可以知道

只要外面有任何事物佔據了私有作用域裡面的東西就會產生閉包(保存該函數作用域)

所以其實我們要產生閉包的核心觀念是要讓外界竊聽到函數內部,這才是為何我們在函數內要return函數的原因(讓外界卡住內存)的原因

所以下面講幾個例子吧!

例子

範例一 最基本閉包

function foo(num) {
  function baz() {
    console.log(++num);    
  }
  return baz
}

let bar = foo(5) // bar卡住了內存
bar(4) // 這個4其實啥意義都沒有
bar(5) // 這個5也沒啥意義

https://ithelp.ithome.com.tw/upload/images/20201004/20124350XX8vgvdsdE.png

改一下範例一看會長怎樣

function foo(num) {
  function baz() {
    console.log(++num);    
  }
  return baz
}

let bar1 = foo(1)
let bar2 = foo(10)

bar1()
bar1()

bar2()
bar2()

可以發現確實可以訪問到num,且我們這邊可以發現會產生不同的值,代表這兩個都會儲存內存,因此我們要小心使用,不然可能會一直浪費內存

https://ithelp.ithome.com.tw/upload/images/20201004/20124350tkZ18atz4U.png

還可以這樣

function foo(num) {
  spy = function () {
    console.log(++num);    
  }
}
foo(5)
spy()
spy()
spy()

https://ithelp.ithome.com.tw/upload/images/20201004/20124350yOqA6B8o9I.png

或是這樣

function foo(a) {
  let bar ={
    geta() {
      console.log(a++);
    }
  }
  return bar
}

let baz1 = foo(2)
baz1.geta() 
baz1.geta()
let baz2 = foo(5)
baz2.geta()
baz2.geta()

https://ithelp.ithome.com.tw/upload/images/20201004/20124350X8uwCc08K8.png


上一篇
Day 19 [其他03] 柯里化與反柯里化
下一篇
Day 21 [其他03] js隱式轉換相關知識
系列文
從技術文章深入學習 JavaScript29
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言