摘要:閉包的問題使用閉包會將局部變量保持在內存中,所以會占用大量內存,影響性能。當閉包的作用域中保存一些節(jié)點時,較容易出現循環(huán)引用,可能會造成內存泄漏。
前言
秋招大大小小若干場面試,幾乎都問了這個問題,當然不知道閉包也不能說會js。所以,不要逃避,好好地來梳理一下。
閉包是什么?來看看MDN官網上關于閉包的定義:
A closure is the combination of a function and the lexical environment within which that function was declared.
閉包是函數以及函數聲明所在的詞法環(huán)境的組合。這個定義有點干澀,個人認為可以結合閉包的實際場景簡單地這么理解:函數調用結果返回一個子函數,同時該子函數使用了外部函數的局部變量,使得變量保存在內存中,形成了閉包。
舉個例子?舉個最簡單的栗子~
function func(){ var count = 0; return function(){ console.log(count++); } } var fn = func(); fn(); // 0 fn(); // 1
func 函數執(zhí)行返回一個函數,其中調用了func的局部變量count,導致func函數執(zhí)行完成后,count變量仍保留在內存空間,未被銷毀,形成了閉包。
閉包的作用?從上一個例子,可以看到閉包的特點是讀取函數內部局部變量,并將局部變量保存在內存,延長其生命周期。利用這個特點可以使用閉包實現以下功能:
解決類似循環(huán)綁定事件的問題
在實際開發(fā)中,經常會遇到需要循環(huán)綁定事件的需求,比如上一篇博客的例子,在id為container的元素中添加5個按鈕,每個按鈕的文案是相應序號,點擊打印輸出對應序號。
其中第一個方法很容易錯誤寫成:
var container = document.getElementById("container"); for(var i = 1; i <= 5; i++) { var btn = document.createElement("button"), text = document.createTextNode(i); btn.appendChild(text); btn.addEventListener("click", function(){ console.log(i); }) container.appendChild(btn); }
雖然給不同的按鈕分別綁定了事件函數,但是5個函數其實共享了一個變量 i。由于點擊事件在 js 代碼執(zhí)行完成之后發(fā)生,此時的變量 i 值為6,所以每個按鈕點擊打印輸出都是6。
為了解決這個問題,我們可以修改代碼,給各個點擊事件函數建立獨立的閉包,保持不同狀態(tài)的i。
var container = document.getElementById("container"); for(var i = 1; i <= 5; i++) { (function(_i) { var btn = document.createElement("button"), text = document.createTextNode(_i); btn.appendChild(text); btn.addEventListener("click", function(){ console.log(_i); }) container.appendChild(btn); })(i); }
注:解決這個問題更好的方法是使用 ES6 的 let,聲明塊級的局部變量。
封裝私有變量
經典的計數器例子:
function makeCounter() { var value = 0; return { getValue: function() { return value; }, increment: function() { value++; }, decrement: function() { value--; } } } var a = makeCounter(); var b = makeCounter(); b.increment(); b.increment(); b.decrement(); b.getValue(); // 1 a.getValue(); // 0 a.value; // undefined
每次調用makeCounter函數,環(huán)境是不相同的,所以對b進行的increment/decrement操作不會影響a的value屬性。同時,對value屬性,只能通過getValue方法進行訪問,而不能直接通過value屬性進行訪問。
閉包的問題?使用閉包會將局部變量保持在內存中,所以會占用大量內存,影響性能。所以在不再需要使用這些局部變量的時候,應該手動將這些變量設置為null, 使變量能被回收。
當閉包的作用域中保存一些DOM節(jié)點時,較容易出現循環(huán)引用,可能會造成內存泄漏。原因是在IE9以下的瀏覽器中,由于BOM 和DOM中的對象是使用C++以COM 對象的方式實現的,而COM對象的垃圾收集機制采用的是引用計數策略,當出現循環(huán)引用時,會導致對象無法被回收。當然,同樣可以通過設置變量為null解決。
舉例如下:
function func() { var element = document.getElementById("test"); element.onClick = function() { console.log(element.id); }; }
func 函數為 element 添加了閉包點擊事件,匿名函數中又對element進行了引用,使得 element 的引用始終不為0。解決辦法是使用變量保存所需內容,并在退出函數時將 element 置為 null。
function func() { var element = document.getElementById("test"), id = element.id; element.onClick = function() { console.log(id); }; element = null; }參考文章
閉包
JavaScript設計模式與開發(fā)實踐
閉包會造成內存泄漏嗎?
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.hztianpu.com/yun/89136.html
摘要:建筑的頂層代表全局作用域。實際的塊級作用域遠不止如此塊級作用域函數作用域早期盛行的立即執(zhí)行函數就是為了形成塊級作用域,不污染全局。這便是閉包的特點吧經典面試題下面的代碼輸出內容答案個如何處理能夠輸出閉包方式方式下一篇你不知道的筆記 下一篇:《你不知道的javascript》筆記_this 寫在前面 這一系列的筆記是在《javascript高級程序設計》讀書筆記系列的升華版本,旨在將零碎...
摘要:但是,必須強調,閉包是一個運行期概念。通過原型鏈可以實現繼承,而與閉包相關的就是作用域鏈。常理來說,一個函數執(zhí)行完畢,其執(zhí)行環(huán)境的作用域鏈會被銷毀。所以此時,的作用域鏈雖然銷毀了,但是其活動對象仍在內存中。 學習Javascript閉包(Closure)javascript的閉包JavaScript 閉包深入理解(closure)理解 Javascript 的閉包JavaScript ...
摘要:閉包是怎么通過作用域鏈霸占更多內存的本文是作者學習高級程序設計第一小節(jié)的一點個人理解,詳細教程請參考原教材。函數執(zhí)行過程創(chuàng)建了一個函數的活動對象,作用域鏈的最前端指向這個對象。函數執(zhí)行完畢返回值后執(zhí)行環(huán)境作用域鏈和活動對象一并銷毀。 JavaScript 閉包是怎么通過作用域鏈霸占更多內存的? 本文是作者學習《JavaScript 高級程序設計》7.2第一小節(jié)的一點個人理解,詳細教程請...
摘要:一前言這個周末,注意力都在學習基礎知識上面,剛好看到了閉包這個神圣的東西,所以打算把這兩天學到的總結下來,算是鞏固自己所學。因此要注意閉包的使用,否則會導致性能問題。五總結閉包的作用能夠讀取其他函數內部變量。 # 一、前言 這個周末,注意力都在學習基礎Js知識上面,剛好看到了閉包這個神圣的東西,所以打算把這兩天學到的總結下來,算是鞏固自己所學。也可能有些不正確的地方,也請大家看到了,麻...
摘要:從最開始的到封裝后的都在試圖解決異步編程過程中的問題。為了讓編程更美好,我們就需要引入來降低異步編程的復雜性。異步編程入門的全稱是前端經典面試題從輸入到頁面加載發(fā)生了什么這是一篇開發(fā)的科普類文章,涉及到優(yōu)化等多個方面。 TypeScript 入門教程 從 JavaScript 程序員的角度總結思考,循序漸進的理解 TypeScript。 網絡基礎知識之 HTTP 協議 詳細介紹 HTT...
閱讀 3496·2023-04-25 22:44
閱讀 1045·2021-11-15 11:37
閱讀 1704·2019-08-30 15:55
閱讀 2709·2019-08-30 15:54
閱讀 1162·2019-08-30 13:45
閱讀 1489·2019-08-29 17:14
閱讀 1937·2019-08-29 13:50
閱讀 3511·2019-08-26 11:39