本文目錄一覽:
PHP中的閉包有什麼用處
閉包其實就是外部函數定義的內部函數。
閉包的作用及好處:閉包給訪問外部函數定義的內部變量創造了條件。也將關於函數的一切封閉到了函數內部,減少了全局變量,這也是閉包的真實含義。
閉包的理解
集合 S 是閉集當且僅當 Cl(S)=S(這裡的cl即closure,閉包)。特別的,空集的閉包是空集,X 的閉包是 X。集合的交集的閉包總是集合的閉包的交集的子集(不一定是真子集)。
閉包是什麼
有限多個集合的並集的閉包和這些集合的閉包的並集相等;零個集合的並集為空集,所以這個命題包含了前面的空集的閉包的特殊情況。無限多個集合的並集的閉包不一定等於這些集合的閉包的並集,但前者一定是後者的父集。
若 A 為包含 S 的 X 的子空間,則 S 在 A 中計算得到的閉包等於 A 和 S 在 X 中計算得到的閉包(Cl_A(S) = A ∩ Cl_X(S))的交集。特別的,S在 A 中是稠密的,當且僅當 A 是 Cl_X(S) 的子集。
閉包包含自由(未綁定到特定對象)變量;這些變量不是在這個代碼塊內或者任何全局上下文中定義的,而是在定義代碼塊的環境中定義(局部變量)。
“閉包” 一詞來源於以下兩者的結合:要執行的代碼塊(由於自由變量被包含在代碼塊中,這些自由變量以及它們引用的對象沒有被釋放)和為自由變量提供綁定的計算環境(作用域)。
閉包
在PHP、Scala、Scheme、Common Lisp、Smalltalk、Groovy、JavaScript、Ruby、 Python、Go、Lua、objective c、swift 以及Java(Java8及以上)等語言中都能找到對閉包不同程度的支持。
為什麼需要閉包?閉包是什麼概念?
閉包的英文對應的是Closure,如果要單純的討論這個概念的話就要提到和圖靈機起名的大名鼎鼎的lambda演算(lamdba calculus)。儘管lamdba的概念並不是本文的重點,但是閉包概念的目的便是支持lamdba的實現。如果你單獨地在百度對進行搜索閉包的話,你會發現大部分都是js相關的內容,主要是js本身就只用閉包的這個概念。但是閉包並不僅限於js,而是一個通用的概念。借用wiki中有點抽象的定義來說的話,閉包就是:
在計算機科學中,閉包(英語:Closure),又稱詞法閉包(Lexical Closure)或函數閉包(function closures),是引用了自由變量的函數。
簡單來說就是當一個方法引用了方法局部變量外的變量時,它就是一個閉包。而如果根據這個定義繼續延展的話,就可以得到另外的一種描述方法:
閉包是由函數和與其相關的引用環境(方法外變量)組合而成的實體。
儘管給出了兩種關於閉包是什麼的定義,但是閉包本身的概念還是較為的抽象。比如直接給例子,我們可以先討論一下為什麼需要閉包。
為了理解閉包,我們可以先假設這樣的一種場景:
我有一個方法A,其中有私有變量。我想在別的方法中可以直接訪問並使用它,但是又不想直接暴露它。
這時候,就可以在A建立一個內部類B來訪問私有變量。那麼這時候,這個內部類中你所可以訪問B的方法,就和A中的私有變量形成了閉包。此時B已經捕獲了A中的變量。即便是A對象銷毀了,被B捕獲的私有變量仍然不會釋放。
所以可以理解,如果希望系統 方法和當前環境的上下文綁定 的話,就可以使用閉包。
儘管有些不恰當,但是我們可以再換一個實際的場景來舉個例子幫助你理解:
當你在購物的時候,你選中了一件商品,然後下單。這時候你的訂單就已經綁定了你選中的商品。那麼你的購買行為就和商品組成了閉包。這時候即便商家下架商品,也不會影響到商品後續的發貨簽收。並且這些邏輯只需要根據購買行為中的信息進行判斷就可以了,而不用關心商家什麼時候把商品下架,這寫邏輯發生時候的商品價格是什麼。
結合上面的例子,我們可以發現,當一個方法和其調用的外部環境形成閉包的時候,由於外部環境已經確定,那麼我們就不用再關心外部環境了,而只用關心方法本身。
所以針對我們為什麼需要閉包,我給出的答案是:
使用閉包的設計方式,由於閉包本身已經包含了上下文信息,所以可以對北向功能調用(用戶)屏蔽由於環境而引發的複雜處理和交互成本。
對於Java來說,可以理解為主要是兩種的閉包方式:
其中內部類除了本身的內部類還有局部內部類、匿名內部類。我們以最簡潔的匿名內部類來舉例:
此時say方法中便捕獲了length屬性,而如果你使用的是足夠版本的IDE的話,獲取還會提示你:
Anonymous new ISay() can be replaced with lambda
替換後:
那麼這時候你就會發現,此時return的對象就是一個通過匿名後直接描述的函數,而這個函數同時還關聯了上下文之外的環境信息。
而在java8中lambda的中我們可以通過函數式編程接口直接接收這種閉包,這些接口常用的為:
關於函數式接口本文就不展開了,但是利用函數式接口,我們就可以這樣傳遞一個閉包:
此時say返回的就不是String的結果了,而是直接將閉包本身返回了。而這個閉包在創建的時候就已經綁定了所需要的環境屬性。所以我們可以在需要的時候再調用閉包,並且不用再關心它到底依賴了其他哪些變量:
當然你可能會角色,這個length似乎沒有什麼用呀,那如果我們換一個形式:
使用Spring的依賴注入後,那麼這個Closure類本身可能是各種策略模式策略器中的一個,策略器返回的是一個已經關聯了具體策略路由的閉包。而當這個方法提供出去的時候,後續的調用者只需要知道這個閉包是可以針對文本進行處理的就可以,而至於之前是使用的什麼策略它則不用關心。因為這些細節都已經通過閉包屏蔽了。
僅僅談論好的而對問題閉口不談確實不好,雖然閉包提供了強大的功能,可以對業務細節進行屏蔽,對系統進行接耦拆分。但是閉包本身確實有一些問題需要留意:
可以發現,這兩個問題都是由於閉包本身的優點而產生的。由於閉包關聯了環境信息,所以其讓環境信息中對象的生命周期變長,這對於系統性能的維護以及jvm的垃圾回收都有負面因素。而同時因為不同於一般的編碼風格,閉包的使用需要開發人員對實體進行抽象,才能比較好地實現。總結來說,對於開發人員本身有一定要求。
平時我們也經常使用lambda表達式來處理一些業務邏輯,偶爾會出現一下的情況:
先不管這段代碼的實現業務背景是什麼,但是IDE會提示在userIds.get(i)中的i提示的信息為:
Variable used in lambda expression should be final or effectively final
結合了上文中關於閉包的內容,我們就不難理解。由於閉包是要關聯外部環境變量,在這部分代碼中關聯的是索引變量i,但是因為i本身是局部變量,無法保證關聯環境的穩定性(我自己的理解),所以java編譯器會強制的要求當閉包關聯的是局部變量的時候,需要添加final關鍵字,而在進行final關鍵字從而保證該變量的內存引用不會發生改變。從本章的代碼上結論上看則是進行了一次內存拷貝,來保證每個閉包中關聯的環境變量不會改變,修改後的代碼為:
閉包本身是一種面向抽象編程,屏蔽細節的設計原則。在良好的設計下,可以通過閉包來屏蔽對於環境信息的感知,從而簡化外部對於系統理解的成本,提高系統的易用性。
原創文章,作者:IOVH,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/143504.html