摘要:從我年開始接觸前端,知道閉包這個詞,已經過去兩年了。概念閉包,在高級程序設計里面是這樣介紹的閉包是指有權訪問另一個作用域中的變量的函數。這樣形成的閉包雖然可以使外部可以訪問到內部的函數,但是導致了原有的作用域鏈不釋放,會造成內存泄漏。
從我16年開始接觸前端,知道閉包這個詞,已經過去兩年了。這兩年里,閉包這個概念我在很多地方了解過,卻實在沒有真的理解,久而久之,變成了一塊心病。這不,趁著現在項目告一段落的時間,我又開始折騰它了,在網易云課堂上找了些資源來看。哈哈,可能是因為已經做了些項目的緣故,這次終于能理解它到底是個什么玩意兒了。
概念閉包,在《javascripts高級程序設計》里面是這樣介紹的:閉包是指有權訪問另一個作用域中的變量的函數。額。。這句話我以前看過很多遍,但依然不是很懂,只知道它是跟作用域有關。現在我知道了,如果這句話換成:但凡是內部的函數被保存到了外部,必定生成閉包。這樣就容易理解多了不是。
我們以下面的這個代碼塊為例:
function a() { const num = 100; function b () { num++; console.log(num); } return b; } const demo = a(); demo(); demo();
我們先執行上述代碼,看看結果是什么:
為什么是這樣呢?
a()執行的結果是返回b,所以demo指的是b,則demo()指的是b();也就是說原先在a里面的b,在b的外部執行;
我們已經知道了作用域和作用域鏈的概念,上面代碼的作用域是這樣的:
a執行完,返回了b,此時的b只是聲明,但還沒調用,所以沒有形成自己的AO,但作用域鏈和 a doing 時是一樣的,所以雖然 a() 的作用域被銷毀了,但是相同的一份卻被b保存到了外面。這也就是內部函數被保存到了外面形成閉包的本質。這樣也不難理解為什么上述代碼打印出來的值是那樣的了:
執行第一個demo()時,也即是執行 b(),由于b保存了a的作用域鏈,所以也可以訪問到 num ,執行 b() 后,加一;
那為什么第二次執行 demo(),打印出的值還是有自增了呢?這是因為操作的都是保存在 b 里的 num ,雖然每次調用 demo() 都會形成新的作用域鏈,但是num,卻是每次從上一次的作用域鏈直接 copy到當前作用域鏈中的。
這樣形成的閉包雖然可以使外部可以訪問到內部的函數,但是導致了原有的作用域鏈不釋放,會造成內存泄漏。(內存泄漏的意思就是占用內存,可用內存資源變少了)。所以如果不是特殊需要,應盡量防止這種情況發生。
并且,作用域鏈的配置機制引出了一個值得注意的副作用:即閉包只取得包含函數中任何變量最后一個值,比如下面這個例子:
function createFunctions() { var result = []; for(let i = 0; i< 10; i++) { result[i] = function() { console.log(i); } } return result; } var myArr = createFunctions(); for(var j = 0; j < 10; j++) { myArr[j](); }
這個函數會返回一個函數數組,表面上看,似乎每個函數都應該有自己的索引值,即會返回:0,1,2...9;但實際上,每個函數都會返回10;這是因為在createFunctions()執行時,for循環跳出的條件是i=10;所以函數返回后,i的值是10 ;而每個result的作用域鏈中都保存這createFunctions()的AO,所以他們引用的都是createFunctions()的i值,所以每個函數內部i的值都是10;
這樣,我們可以創建一個立即執行函數強制讓閉包的行為符合預期:
function createFunctions() { var result = []; for(var i = 0; i < 10; i++) { (function(j) { result[i] = function() { document.write(j + " "); } }(i)); } return result; } var myArr = createFunctions(); for(var j = 0; j < 10; j++) { myArr[j](); }典型應用
下面看看幾個用到閉包的典型例子:
實現共有變量
如累加器:調用多少次,累加多少次,用閉包更加模塊化
function add() { var count = 0; function demo() { count++; console.log(count); } return demo; } var counter = add(); counter();//1 counter();//2 counter();//3
實現緩存
如eater: eat和push保存的都是eater的AO;,所以eat中food改變后。實際上是eater變了,所以也會影響push;
function eater() { var food = ""; var obj = { eat: function() { console.log("eating" + food); food = ""; }, push: function(myFood) { food = myFood; } } return obj;// 相當于返回里面的eat和push操作food; } var eater1 = eater(); eater1.push("banana"); eater1.eat();
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/100262.html
摘要:如何在初學就理解閉包你需要接著讀下去。這樣定義閉包是函數和聲明該函數的詞法環境的組合。小結閉包在中隨處可見。閉包是中的精華部分,理解它需要具備一定的作用域執行棧的知識。 這是本系列的第 4 篇文章。 作為 JS 初學者,第一次接觸閉包的概念是因為寫出了類似下面的代碼: for (var i = 0; i < helpText.length; i++) { var item = he...
摘要:閉包在我理解是一種比較抽象的東西。所以我寫了一篇博文來方便自己理解閉包。那么現在我們可以解釋一下閉包的第一個定義在計算機科學中,閉包是引用了自由變量的函數。循環中創建閉包在我們使用的關鍵字之前,閉包的一個常見問題就出現在循環中創建閉包。 零. 前言 從我開始接觸前端時就聽說過閉包,但是一直不理解閉包究竟是什么。上網看了各種博客,大家對閉包的說法不一。閉包在我理解是一種比較抽象的東西。所...
摘要:閉包是函數內部的子函數能讀取局部變量二閉包的特點函數里面嵌套函數內部函數能訪問外部函數的變量定義的參數和變量不會回收三閉包的前提先明白什么是全局變量和局部變量中聲明變量格式關鍵字變量名標識符。建議在退出函數之前,將不使用的局部變量全部刪除。 一、閉包的概念 閉包是指一個函數能夠訪問其函數外部作用域中的變量。JavaScript閉包是函數內部的子函數能讀取局部變量 二、閉包的特點 函數...
摘要:說了半天,究竟什么是閉包呢閉包就是函數的局部變量集合,只是這些局部變量在函數返回后會繼續存在。彈出上面函數中的函數就是閉包,就是通過建立函數來訪問函數內部的局部變量。閉包會在父函數外部,改變父函數內部變量的值。 JavaScript的閉包 首先聲明,這是一篇面向小白的博客,不過也歡迎各位大牛批評指正,謝謝。 ??其實關于閉包各個論壇社區里都有很多的文章來講它,畢竟閉包是JavaScri...
摘要:說了半天,究竟什么是閉包呢閉包就是函數的局部變量集合,只是這些局部變量在函數返回后會繼續存在。彈出上面函數中的函數就是閉包,就是通過建立函數來訪問函數內部的局部變量。閉包會在父函數外部,改變父函數內部變量的值。 JavaScript的閉包 首先聲明,這是一篇面向小白的博客,不過也歡迎各位大牛批評指正,謝謝。 ??其實關于閉包各個論壇社區里都有很多的文章來講它,畢竟閉包是JavaScri...
閱讀 1698·2021-10-28 09:32
閱讀 605·2021-09-24 09:47
閱讀 2920·2021-09-02 15:11
閱讀 2733·2021-08-09 13:46
閱讀 2884·2019-08-30 15:55
閱讀 1071·2019-08-30 15:54
閱讀 3300·2019-08-29 14:12
閱讀 805·2019-08-26 13:40