什麼是閉包?
閉包是什麼?閉包是Closure,這是靜態語言所不具有的一個新特性。但是閉包也不是什麼複雜到不可理解的東西,簡而言之,閉包就是:閉包就是函數的局部變數集合,只是這些局部變數在函數回傳後會繼續存在。閉包就是是函數的「堆疊」在函數返回後並不釋放,我們也可以理解為這些函數堆疊並不在棧上分配而是在堆上分配,當在一個函數內定義另一個函數就會產生閉包。
閉包= 函數內部建立的函數(或簡稱內部函數) + 函數建立時所處環境資訊
所以閉包並不等於匿名函數,雖然也有人稱這些在函數內部建立的函數為閉包函數,但是我覺得其實不準確。
我們看一下下面這段程式碼:
function init() { var name = "Zilongshanren"; // name 是在 init 函数里面创建的变量 // displayName() 是一个内部函数,即一个闭包。注意,它不是匿名的。 function displayName() { console.log(name); } //当 displayName 函数返回后,这个函数还能访问 init 函数里面定义的变量。 return displayName; } var closure = init(); closure(); Zilongshanren undefined
displayName 是一個在 init 函數內部創建的函數,它攜帶了 init 函數內部作用域的所有信息,例如這裡的 name 變數。當 displayName 函數傳回的時候,它本身就攜帶了當時創建時的環境訊息,也就是 init 函數裡面的 name 變數。
閉包有什麼作用?
在理解什麼是閉包之後,接下來你可能會問:這東西這麼難理解,它到底有什麼用啊?
因為在 Js 裡面是沒有辦法建立私有方法的,它不像 java 或 C++有什麼 private 關鍵字可以定義私有的屬性和方法。 Js 裡面只有函數可以創造出屬於自身的作用域的對象,Js 並沒有塊作用域!這個我後面會再寫一篇文章詳細介紹。
程式老鳥都知道,程式寫得好,封裝和抽像要運用得好!不能定義私有的屬性和方法,意味著封裝和抽像根本沒用。 。 。
無法定義私有的東西,所有變數和函數都 public 顯然有問題, Global is Evil!
閉包是我們的救星!
我們看一下下面這段程式碼:
var makeCounter = function() { var privateCounter = 0; function changeBy(val) { privateCounter += val; } return { increment: function() { changeBy(1); }, decrement: function() { changeBy(-1); }, value: function() { return privateCounter; } } }; var counter1 = makeCounter(); var counter2 = makeCounter(); console.log(counter1.value()); /* Alerts 0 */ counter1.increment(); counter1.increment(); console.log(counter1.value()); /* Alerts 2 */ counter1.decrement(); console.log(counter1.value()); /* Alerts 1 */ console.log(counter2.value()); /* Alerts 0 */ 0 2 1 0 undefined
這裡面的 privateCounter 變數和 changeBy 都是私有的,對於 makeCounter 函數外部是完全不可見的。這樣我們透過 makeCounter 產生的物件就把自己的私有資料和私有方法全部隱藏起來了。
這裡有沒有讓你想到點什麼?
哈哈,這不就是 OO 麼?封裝資料和操作資料的方法,然後透過公共的介面呼叫來完成資料處理。
當然,你也許會說,我用原型繼承也可以實作 OO 呀。沒錯,現在大部分人也正是這麼幹的,包括我們自己。不過繼承這個東西,在理解起來總是非常困難的,因為要理解一段程式碼,你必須要理解它的所有繼承鏈。如果一旦程式碼出 bug 了,這將是非常難調試的。
扯遠了,接下來,讓我們看看如何正確地使用閉包。
如何正確地使用閉包?
閉包會佔用內存,也會影響 js 引擎的執行效率,所以,如果一段程式碼被頻繁執行,那麼要謹慎考慮在這段程式碼裡面使用閉包。
讓我們來看一個創建物件的函數:
function MyObject(name, message) { this.name = name.toString(); this.message = message.toString(); this.getName = function() { return this.name; }; this.getMessage = function() { return this.message; };} var myobj = new MyObject(); var myobj = new MyObject();
每一次被呼叫產生一個新物件的時候,都會產生兩個閉包。如果你的程式裡面有成千上萬個這樣的 MyObject 對象,那麼就會多出很多記憶體佔用。
正確的做法應該是使用原型鏈:
function MyObject(name, message) { this.name = name.toString(); this.message = message.toString(); } MyObject.prototype.getName = function() { return this.name; }; MyObject.prototype.getMessage = function() { return this.message; }; var myobj = new MyObject();
現在MyObject 原型上面定義了兩個方法,當我們透過new 去建立物件的時候,這兩個方法只會在原型上面存有一份。
閉包的效能如何?
閉包也是一個函數,但是它存儲了額外的環境信息,所以理論上它比純函數佔用更多的內存,而且 Js 引擎在解釋執行閉包的時候消耗也更大。不過它們之間的效能差異在 3%和 5%之間(這是 Google 上得到的數據,可能不會太準確)。
但是,閉包的好處一定是大大的。多使用閉包和無狀態編程,讓 Bug 從此遠離我們。
了解閉包,你就能理解大部分 FP 範式的 Js 類別庫及其隱藏在背後的設計想法。當然只有閉包還不夠,你還需要被 FP 和無狀態,lambda calculus 等概念洗腦。
關於js閉包希望大家學完本篇內容之後就有所掌握。
相關推薦:
以上是什麼是 Javascript 的閉包的詳細內容。更多資訊請關注PHP中文網其他相關文章!