觀感度:🌟🌟🌟🌟🌟
口味:冰鎮西瓜
烹飪時間:20min
撩妹守則第一條,女孩子都喜歡童話故事。
那就先來講一個童話故事~
// 有一個公主
// 她生活在一個充滿冒險的奇妙世界裏
// 她遇見了她的白馬王子,帶着她騎着獨角獸環遊世界
// 與龍搏鬥,遇到了會說話的松鼠,以及許多其他幻想的事情。
function princess () {
var adventrures = [];
function princeCharming () {};
var unicorn = {};
var dragons = [];
var squirrel = "Hello!";
// 但她不得不回到她那充滿家務和大人們的單調世界。
return {
// 她經常給身邊的人講她作爲一個公主的奇妙經歷。
story:function () {
return adventures[adventures.length - 1];
}
}
}
// 但他們看到的只是一個小女孩在講述關於魔法和幻想的故事
var littleGril = princess();
littleGril.story();
// 即使大人們知道她是真正的公主,他們也不會相信所謂的獨角獸或龍,因爲他們永遠看不到它們
// 大人們說它們只存在於小女孩的想象中
// 但我們知道真正的真理
// 裏面的小女孩真的是個公主
這個故事來自於stackoverflow的一則回答,看不懂沒關係,等閱讀完本文後,回頭再來看這個故事,你會發現你已經完全瞭解了我的魅力,咳咳@¥%#…………JavaScript
中閉包的魅力。
什麼是閉包?
當函數可以記住並訪問所在的詞法作用域時,就產生了閉包,即使函數是在當前詞法作用域之外執行。 --- 你不知道的JavaScript(上卷)
來個🌰
function demo() {
var a = 1;
return function () {
return a;
}
}
var a = demo();
console.log(a()); // 1
閉包的構成
閉包由兩部分構成:函數,以及創建該函數的環境。
環境由閉包創建時在作用域中的任何局部變量組成。
閉包的本質
閉包其實是JavaScript
函數作用域的副作用產品。
閉包是一種特殊的對象。
所謂有意栽花花不開,無心插柳柳成蔭
,不是JavaScript
故意要使用閉包,而是由於JavaScript
的函數內部可以使用函數外部的變量,這段代碼又剛剛好符合閉包的定義。
在JavaScript
中,外部函數調用之後其變量對象本應該被銷燬,但閉包阻止了它們的銷燬,我們仍然可以訪問外部函數的變量對象。
進一步的說,通常情況下,函數的作用域及其所有變量都會在函數執行結束後被銷燬。但是,如果創建了一個閉包的話,這個函數的作用域就會一直保存到閉包不存在爲止。
function addCalculator (x) {
return function (y) {
return x + y;
}
}
var add1 = addCalculator(1);
console.log(add1(1)); //2
// 釋放對閉包的引用
add1 = null;
console.log(add1(1)); //Uncaught TypeError: add1 is not a function
閉包的應用
我們可以用閉包來做什麼呢?
瞭解Java
的同學可能知道,Java
是支持私有方法的,私有方法只能被一個類中的其他方法所調用,但是JavaScript
沒有提供這種原生支持,所以我們可以通過閉包來模擬私有方法。
私有方法自然有私有方法的好處,私有方法有利於限制對代碼的訪問,而且可以避免非核心的方法干擾代碼的公共接口,減少全局污染。
來個🌰
var calculator = (function(){
var a = 1;
function addCalculator(val){
a += val
}
return {
add1:function() {
addCalculator(1);
},
add2:function() {
addCalculator(2);
},
result:function() {
return a
}
}
})();
console.log(calculator.result()); // 1
calculator.add1();
console.log(calculator.result()); // 2
calculator.add2();
console.log(calculator.result()); // 4
上面這種方式也叫做模塊模式(module pattern)。
使用閉包的注意事項
內存泄漏
因爲閉包可以使函數中的變量都保存在內存中,造成很大的內存消耗,所以如果 不是某些特定的任務需要使用閉包,我們不要濫用它。
很多博客中都提到了這一點,但是其實都是不完全對的。
敲黑板!!!
使用不當的閉包會在IE(IE9)
之前造成內存泄漏問題。因爲它的JavaScript
引擎使用的垃圾回收算法是引用計數法,對於循環引用將會導致GC
(下文會介紹)無法回收垃圾。
關於各個瀏覽器的閉包測試,詳情請見司徒正美-js閉包測試
垃圾回收機制
都9102年了,全國開始實行垃圾分類了,你居然還不知道垃圾回收機制,趕快來補習一下!
垃圾回收也就是GC(Garbage Collection)
。
GC
把程序不用的內存空間視爲垃圾,找到它們並且將它們回收,讓程序員可以再次利用這部分空間。
不是所有的語言都有GC
,一般存在於高級語言中,如Java
、JavaScript
、Python
。那麼在沒有GC
的世界裏,程序員就比較辛苦,只能手動去管理內存,比如在C
語言中我們可以通過malloc/free
,在C++
中的new/delete
來進行管理。
垃圾回收算法
因爲這一部分的內容很多,本文只進行簡單的講解,如果想深入瞭解垃圾回收算法的同學可以在文末獲取學習資料。
GC標記-清除算法
世界上首個值得紀念的GC
算法是GC標記-清除算法
。因爲自其問世以來,一直到半個世紀後的今天,它依然是各種處理程序所用的偉大的算法。
GC標記-清除算法
由標記階段和清除階段構成,標記階段將所有的活動對象做上相應的標記,清除階段把那些沒有標記的對象,也就是非活動對象進行回收。在搜索對象並進行標記的時候使用了深度優先搜索,儘可能的從深度上搜索樹形結構。
優點:
1.算法簡單,實現容易。2.與保守式的GC算法兼容。
缺點:
1.在使用過程中會出現碎片化的情況,如同Windows
的文件系統一樣,導致
無數的小分塊散佈在堆的各個地方。2.分配速度,由於分塊的不連續性,算法每次分配的時候都需要遍歷空閒鏈表爲了找到足夠大的分塊,這樣最糟糕的情況就是遍歷到最後才找到合適的分
塊,影響了分配速度。
引用計數法
這種方法中引入了計數器的概念,通過計數器來表示對象的“人氣指數”,也就是有多少個程序引用了這個對象。當計數器(引用數)爲0時,垃圾立刻被回收。
優點:
1.可以立即回收垃圾。2.最大暫停的時間短。
3.並且沒有必要沿指針查找。
缺點:
1.上文提到過的循環引用無法回收。2.並且實現起來很複雜。
3.計數器值的增減處理十分繁重。
4.同時計數器需要佔很多位,導致內存空間的使用效率大大降低。
軟件工程沒有銀彈,這些缺點也都有相應的辦法進行解決,如果你想深入瞭解垃圾回收算法,可以購買垃圾回收的算法與實現這本書去看,建議支持正版。
歡迎關注我的個人公衆號,文章將同步發送,後臺回覆【福利】即可免費領取海量學習資料。
你的前端食堂,記得按時吃飯。
ps:你也可以後臺回覆垃圾回收即可領取相關資料一份~