亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > JS > 正文

淺談JavaScript閉包

2024-05-06 16:49:43
字體:
來源:轉載
供稿:網友

最近朋友面試被問到了 JS 閉包的問題,本人一時語塞,想起了袁華的一句話:“這道題太難了,我不會做,不會做?。?rdquo;。

JS 閉包屬于面向對象的一個重要知識點,特此本人又開始了一段說走就走的旅程。

閉包就是外層函數的作用域(AO)對象被內層函數所引用,無法被釋放。

上面那句話聽起來可能不是很理解,本人在之前寫過一篇Python 閉包小記》的關于 Python 閉包的一些知識的文章,里面寫了百度百科對于閉包的理解,雖然由于才疏學淺大部分都是引用的他人的知識架構,但語言這種東西都是相通的,我們不需要去記那些晦澀的名詞,對于閉包,作為初學者我們只需知道:

函數作為返回值,函數作為參數傳遞。就可以將其理解為閉包。

話不多說,先上個代碼緩和一下尷尬的氣氛:

function outer() {  var max = 10;  function inner(num) {    if (num > max) {      console.log(num)    }  }  return inner;}var foo = outer();foo(20); // 20

上面代碼滿足函數作為返回值的條件,所以是一個閉包函數。

根據 JS 函數的執行機制,先執行第 10 行的 foo 代碼,在函數執行完之后會被 JS 的垃圾回收機制將 outer 函數回收,但是在執行到第 3 行的時候我們發現 outer 函數內部又出現了一個 inner 函數,且 inner 函數里引用著 outer 函數的 max = 10; 的變量,這就無法被回收并且留在了內存里,當執行到第 11 行時由于 outer 函數內的 max = 10; 被留在內存中,所以會被 inner 函數調用,并滿足 if 條件判斷,所以輸出 20;

以上我們實現了一個簡單的閉包函數,但是卻產生了一個問題,那就是無法被釋放的對象留在了內存當中,造成了不必要的內存開銷。

再看如下代碼:

var max = 10,  foo = function (num) {    if (num > max) {      console.log(num);    }  };(function (bar) {  var max = 100;  bar(20)})(foo);  // 20

上面代碼滿足函數作為參數傳遞的條件,所以是一個閉包函數。

函數 foo 作為一個參數被傳入函數中,賦值個 bar 參數,當執行到 bar() 函數時,函數內部的 max 并不是 100,而是 10,這似乎匪夷所思。我們暫且將 7 — 10 行的函數叫 “父作用域”,其余叫“全局作用域”,當執行到 bar(20) 時,函數去執行第 2 行的代碼,此時 foo 函數內部的 max 要去取值,而 max = 10; 正好在他所在的 “全局作用域” 內,所以會取 max = 10; 的值而不是 max = 100; 的值。由此可見,取值時要去創造這個函數的作用域內取值,而不是所謂的 “父作用域” 或者離函數近的地方取值。

我們再來看一段代碼:

var num = 20;function outer() {  var max = 10;  function inner() {    if (num > max) {      console.log(num);    }  }  return inner;}var foo = outer(),  num = 30;foo(); //30

上面的代碼在看完上面的解釋后可以得知它是一個閉包函數,且定義了一個全局變量 num,最初定義為 num = 20,當代碼執行到第 11 行時去調用執行第 2 行,待第 11 行執行完畢后執行第 12 行,此時將全局的 num = 20; 變為了 num = 30; 再執行第 13 行,此時執行時調用 inner 函數時,從輸出結果我們可以看出調用的 num 為之后賦值的 30,

由此可見全局的 num 變量被污染了。

我們再來看下一段代碼:

function outer() {  var max = 10;  function inner(num) {    if (num > max) {      console.log(num);    }  }  return inner;}var foo = outer(),  max = 100;foo(20);  //20

上面的代碼中當函數執行時,先執行第 10 行,然后調用執行第 1 行的函數,此時將 max 賦值為 10,但需要注意的是此時的 max = 10;并不是在全局作用域內,而是在 outer() 函數的作用域內,執行完第 10 行再執行第 11 行,此時將 max 賦值為 100,但需要注意的是此時的 max = 100;是在全局作用域內。所以在執行到第 12 行代碼的時候調用執行 inner() 函數并將參數 20 傳入,輸出結果為 20,由此可見outer() 函數作用域內的對象 num 并沒有被全局的對象 num 所污染。

由以上四段代碼我們初步了解了一些閉包的基本特征,但是由于才疏學淺,怕總結的不夠全面,這時突然想到了東東大神的筆記,于是上網搜到了一些,下面就將其再歸納總結一下。

 

閉包:既重用一個變量,又保護變量不被污染的一種機制。

為什么使用閉包:

全局變量和局部變量都具有不可兼得的優缺點。

全局變量:

  1. 優: 可重用,
  2. 缺: 易被污染。

局部變量:

  1. 優: 僅函數內可用,不會被污染
  2. 缺: 不可重用!

何時使用:

只要既重用一個變量,又保護變量不被污染時。

如何使用:

  1. 1. 用外層函數包裹要保護的變量和內層函數。
  2. 2. 外層函數將內層函數返回到外部。
  3. 3. 調用外層函數,獲得內層函數的對象,保存在外部的變量中——形成了閉包。

閉包形成的原因:

外層函數調用后,外層函數的函數作用域(AO)對象無法釋放,被內層函數引用著。

閉包的缺點:

  1. 比普通函數占用更多的內存。
  2. 解決:閉包不在使用時,要及時釋放。
  3. 將引用內層函數對象的變量賦值為null。

結合上面舉的四段代碼栗子和東東的筆記,我們已經對閉包有了一個形象的認識,但是要到達全面理解的程度,只能說革命尚未成功,同志仍需努力。

令人可喜的是在網上又查到了東東對于閉包更形象的圖形講解,看完之后相信大家對閉包會有更加深刻的理解。

先來一段代碼緩和一下字多的尷尬:

//1. 用外層函數包裹要保護的變量和內層函數function outer() {  var i = 1;  //2. 外層函數返回內層函數對象到外部  return function () {    console.log(i++);      }}//3. 調用外層函數獲得內層函數對象var getNum = outer(); //getNum:function(){ console.log(i++); }getNum();//1getNum();//2i = 1;getNum();//3getNum();//4

上面的代碼是定義了一個 outer() 外層函數,外層函數的作用域內定義了 i = 1;的變量,內部返回了一個函數,這就形成了閉包。當代碼執行到第 10 行,其實就返回了一個 outer() 函數的內部函數,執行一次 getNum(),由于打印的是 i++ ,所以輸出結果為 1,(注:如果打印的是 ++i,輸出結果為 2 )。再執行一次 getNum(),由于之前 i 已經執行過一次 i++,所以此次執行結果為 2,再在全局設置 i = 1,再次執行 getNum() 兩次,執行結果分別為 3 和 4,說明全局設置的 i = 1,并沒有覆蓋 outer() 函數作用域內的 i 值,outer() 函數內的 i 值被很好的保護起來并得到了重用。

我們來看看東東對上面代碼的圖形化分析:

JavaScript,閉包

如上圖:在 JavaScript 中有一個執行環境棧(ECS)概念,注:ECS = 局部EC + 全局EC,所有的函數都要通過進棧、出棧來執行,執行環境棧中有一個自帶的 main() 函數的全局EC 指向全局的 window 作用域,它會指向全局的 window 對象,代碼運行到紅線部分的時候,執行環境棧中僅有一個全局執行環境 window,此時 window 中有兩個全局變量(標識符):outer 、getNum,其中 outer() 函數開辟了一塊內存用于存儲所執行的方法,并且通過 scope 記住它的父級。

JavaScript,閉包

如上圖:當執行 outer() 函數時,outer() 相當于局部EC 進入執行環境棧,此時 outer() 會開辟一塊屬于自己的作用域(AO),里面定義了 i = 1,的環境變量。 由于 window 中引用著 i 對象,所以 outer 的 AO 會指向 window,同時 getNum 會調用 outer() 函數并返回一個方法,所以會開辟一塊內存用于存儲所執行的方法,該方法中又有 i 變量指向 outer 的 AO,綠色線三方互相牽連。

JavaScript,閉包

如上圖:當執行環境棧中的 outer() 函數執行完出棧時,理論上 outer 的 AO,即藍色框應該被垃圾回收機制所回收,但是由于閉包作用,這塊就被留了下來,閉包至此形成。

JavaScript,閉包

如上圖:當 outer() 函數出棧,getNum() 函數進棧,getNum 開辟屬于自己的作用域(AO),且執行了一次 i++ 。此時輸出結果為 1。

JavaScript,閉包

如上圖:當 getNum() 函數出棧時,自己多開辟的作用域被回收,但是 outer 的作用域由于閉包作用依然留在內存中,且變為了 i = 2。

JavaScript,閉包

如上圖:再次執行 getNum() 函數,相當于 getNum() 函數再次入棧出棧,原來由于閉包作用保留的 i = 2 再次做 ++ 運算。

JavaScript,閉包

如上圖:再往下執行 i = 1,即在全局 window 當中添加了 i 對象。此時 outer 作用域內的 i 由于上一次的 ++ 變為了 3。

JavaScript,閉包

如上圖:第三次執行 getNum() 函數,此時大家應該懂得該怎么執行了吧,getNum() 并不會去全局的 window 中去取 i = 1 使用,而是去所創造它的作用域去值,即 i = 3 做 ++ 運算。

至此閉包的運行流程就全部介紹完了,大家是不是對于閉包有了一個比較清晰的了解了。

別急,還差那么一點點,那就是主動釋放閉包所產生的內存。如下

//1. 用外層函數包裹要保護的變量和內層函數function outer() {  var i = 1;  //2. 外層函數返回內層函數對象到外部  return function () {    console.log(i++);    i = null;  }}//3. 調用外層函數獲得內層函數對象var getNum = outer(); //getNum:function(){ console.log(i++); }getNum(); //1getNum(); //0i = 1;getNum(); //0getNum(); //0

在執行完第一次 getNum() 函數時我們就將 i 變量設為 null,再次執行 getNum() 函數時發現所得結果已經變為 0 了,說明 outer() 函數內的 i 變量內存已經被釋放了?。。?/p>

至此 JavaScript 閉包的全部內容就講解完畢了,以上內容如有紕漏請各位大神批評指正。

好記性不如爛筆頭,特此記錄,與君共勉!

以上所述是小編給大家介紹的JavaScript閉包詳解整合,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對VeVb武林網網站的支持!


注:相關教程知識閱讀請移步到JavaScript/Ajax教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
精品国产拍在线观看| 精品久久中文字幕久久av| 91青草视频久久| 亚洲成在人线av| 51ⅴ精品国产91久久久久久| 2018中文字幕一区二区三区| 欧美高清视频免费观看| 久久九九全国免费精品观看| 91爱爱小视频k| 亚洲美女av电影| 最好看的2019年中文视频| 国产精品永久免费视频| 亚洲色图欧美制服丝袜另类第一页| 亚洲综合在线中文字幕| 久久久这里只有精品视频| 中文字幕在线观看亚洲| 国产精品久久久久9999| 国产精品激情av电影在线观看| 日韩av免费看| 欧美人在线视频| 爽爽爽爽爽爽爽成人免费观看| 久久精品久久久久电影| 欧美性生交xxxxx久久久| 全球成人中文在线| 91国产精品91| 欧美午夜视频在线观看| 欧美亚洲一区在线| 琪琪亚洲精品午夜在线| 最新的欧美黄色| 欧美极品第一页| 欧美成人sm免费视频| 国产丝袜精品第一页| 亚洲一区第一页| 九九热精品视频在线播放| 国产v综合ⅴ日韩v欧美大片| 日韩精品中文在线观看| 亚洲高清福利视频| 亚洲国产一区二区三区四区| 欧美日韩中文字幕在线| 国产精品久久久久久久久久三级| 日韩中文综合网| 亚洲一区二区三| 亚洲精品国产精品国自产在线| 欧美日韩在线视频一区二区| 欧美一区深夜视频| 精品成人久久av| 久久久久久尹人网香蕉| 日韩在线视频二区| 日韩免费高清在线观看| 久久九九有精品国产23| 91精品国产91久久久久久吃药| 92看片淫黄大片欧美看国产片| 911国产网站尤物在线观看| 一区二区三区四区在线观看视频| 亚洲精品成人久久电影| 有码中文亚洲精品| 97香蕉超级碰碰久久免费的优势| 亚洲少妇激情视频| 国产91成人在在线播放| 亚洲a在线观看| 97久久超碰福利国产精品…| 欧美亚州一区二区三区| 亚洲精品免费一区二区三区| 日韩综合视频在线观看| y97精品国产97久久久久久| 中文字幕一区二区三区电影| 亚洲精品国产电影| 国产91精品网站| 日韩欧美有码在线| 成人有码在线视频| 日韩av电影在线网| 国产精品成人一区二区| 色综合久久中文字幕综合网小说| 热99精品里视频精品| 亚洲精品mp4| 亚洲午夜久久久久久久| 亚洲激情 国产| 亚洲成人av片| 国产成人精品久久二区二区| 蜜臀久久99精品久久久无需会员| 国内揄拍国内精品少妇国语| 欧美国产日本在线| 日韩av在线影视| 午夜精品久久久久久久久久久久| 亚洲成人精品久久| 91福利视频网| 国产精品久久久久久影视| 一区二区三区www| 亚洲国内精品视频| 国产精品久久久久久超碰| 国产精品999999| 国产成人一区二区在线| 欧美在线激情视频| 亚洲精品国产福利| 欧美日韩在线另类| 91成品人片a无限观看| 欧美野外wwwxxx| 欧美主播福利视频| 18性欧美xxxⅹ性满足| 欧美孕妇性xx| 亚洲成av人乱码色午夜| 久久久久久综合网天天| 成人激情视频在线| 丁香五六月婷婷久久激情| www.日韩不卡电影av| 亚洲人成绝费网站色www| 成人福利在线观看| 久久99热精品这里久久精品| 日韩av电影免费观看高清| 97久久伊人激情网| 日韩欧美a级成人黄色| 国产一区二区三区丝袜| 九九热这里只有在线精品视| 成人精品网站在线观看| 亚洲成年人在线| 午夜剧场成人观在线视频免费观看| 国产精品影院在线观看| 2018国产精品视频| 久久久av一区| 亚洲r级在线观看| 欧美日韩黄色大片| 大伊人狠狠躁夜夜躁av一区| 亚洲视频axxx| 51ⅴ精品国产91久久久久久| 日韩精品欧美国产精品忘忧草| 4388成人网| 久久天天躁日日躁| 日韩精品免费电影| 综合久久五月天| 国产一区二区欧美日韩| 欧美成人免费观看| 亚洲美女福利视频网站| 国产精品久久久久久久av电影| 美女久久久久久久| 亚洲精品欧美一区二区三区| 97成人精品视频在线观看| www高清在线视频日韩欧美| 欧美在线影院在线视频| 亚洲欧美日韩中文视频| 精品国产91久久久| 97超碰色婷婷| 在线一区二区日韩| 国产玖玖精品视频| 欧美日韩免费在线| 亚洲午夜久久久影院| 精品久久久久久久久久久久| 欧美性视频精品| 欧美色videos| 在线亚洲男人天堂| 欧美午夜精品在线| 中日韩美女免费视频网址在线观看| 亚洲理论片在线观看| 69av在线视频| 亚洲精品乱码久久久久久按摩观| 91禁外国网站| 国产精品高潮呻吟视频| 国产精品黄页免费高清在线观看| 午夜精品www| 91精品中国老女人| 日韩大胆人体377p| 亚洲美女激情视频| 国内精久久久久久久久久人| 久久久视频免费观看| 亚洲第一黄色网|