到底該如何理解閉包?
最近在看scala,裡面講到了閉包以及它的一些好處,但就是沒看明白什麼才算做是閉包,請用通俗易懂的語言解釋一下吧
相關問題:到底什麼是閉包? - 前端開發
移步:直觀地理解Javascript closure的本質
閉包出現是因為lexical scope,閉包是由函數和環境組成,Scala應該支持函數作為參數或返回值,這時如果沒有閉包,那麼函數的free 變數就會出錯
Closures
「An object is data with functions. A closure is a function with data.」 — John D. Cook
已知一個函數 f( x ) = x + i ,讓你求 f(3) = 3+i。
分析:要得到最終的函數值,你必須知道i的值。i稱作開放項(「開」著的,對應閉包的「閉」),若上文中定義了「 inti = 1」 ,則可以得到f(3) = 3+1 =4 , 即函數值若想被創建必須捕獲i的值,這一過程可以被理解為做對函數執行「關閉」操作,所以叫閉包。
閉包就是帶有狀態的函數, Effective C++ 3rd 里關於單例模式的實現方式,可以認為就是一種閉包.
樓里大部分的回答是不準確或錯誤的。
最簡潔、直擊要害的回答,我能想到的分別有這麼三句(版權屬於 @張恂老師 ):
1、閉包是一個有狀態(不消失的私有數據)的函數。2、閉包是一個有記憶的函數。3、閉包相當於一個只有一個方法的緊湊對象(a compact object)。上面這三句話是等價的,而其中第 3 句最精妙,可以指導何時、如何用好閉包,後面我會詳細分析。
@張恂老師 在這篇文章里主要從大多數閉包使用者 Developer 的角度作了精闢、透徹、獨到的分析:
到底什麼是閉包 - 張恂老師的文章 - 知乎專欄
看了這文,相信很多初學者對於「什麼是當代函式編程語言中的閉包」會有一種全新的、豁然開朗的認識。
樓里只看到一兩個正解(除了我之外)。
@pikachu 的分析很贊:
能說出這句話的人才是真懂閉包的。閉包出現是因為lexical scope,閉包是由函數和環境組成,Scala應該支持函數作為參數或返回值,這時如果沒有閉包,那麼函數的free 變數就會出錯
謝邀以前有個ppt里分享過函數與閉包:分享ppt: scala中的函數與閉包還有這篇blog里的例子是閉包中的典型的綁定問題:閉包變數綁定問題
希望對你有用
看書學這玩意屬於白搭。大概了解一下開始寫代碼吧。寫開了你才能了解scala的美
閉包就是(編譯器創建的)執行一段代碼所需要的上下文。
好了,這樣理解就行了,繼續看下去吧。從某種意義上來說,所有的函數都是閉包,c/c++里的函數是特殊的閉包,沒有自由變數,無狀態,保存這些狀態或者自由變數,python里即nonlocal,lua里upvalue。在c++11 以前可以用類實現模仿有狀態的閉包。
首先,閉包是一個函數,其次,閉包是有權訪問另一個函數作用域中的變數的函數。
閉包是一個綁定了內部引用的變數的函數。
通常我們都是手動去管理狀態;而閉包是讓語言去管理。JavaScript 里的閉包是什麼?應用場景有哪些? - Barret李靖的回答1.柯里化是把接受多個參數的函數變換成接受一個單一參數(最初函數的第一個參數)的函數,並且返回接受餘下的參數而且返回結果的新函數的技術。
scala&> def curriedSum(x:Int)(y:Int) = x + y
curriedSum: (x: Int)(y: Int)Intval twoPlus = curriedSum(2) _
twoPlus: Int =&> Int = &scala&> twoPlus(2)
res3: Int = 42.閉包,作為返回值的函數中,引用了外層函數中的變數,導致外層函數的執行環境無法釋放,外層函數就像一個保護罩一樣保護著被引用變數不被修改。
scala&> def first(x:Int) = (y:Int) =&> x + yfirst: (x: Int)Int =&> Intval second=first(1) //變數x一直存在,但由於外層函數的屏蔽無法被修改
second: Int =&> Int = &
scala&> second(2)
res1: Int = 33.偏函數是只對函數定義域的一個子集進行定義的函數。 scala中用scala.PartialFunction[-T, +S]類來表示。比如定義了一個函數:def sum(x: Int)(y: Int) = x + y, 當調用sum的時候,如果不提供所有的參數或某些參數還未知時,比如sum _ , sum(3)(_: Int), sum(_: Int)(3), 這樣就生成了所謂的部分應用函數。部分應用函數只是邏輯上的一個表達,scala編譯器會用Function1, Function2這些類來表示它.javascript閉包--2分鐘徹底理解
簡單理解,閉包就是提供了一種訪問函數局部變數的方法。
第一次回答問題,本人正在看scala,然後對閉包也有疑惑,但是好像編譯一下就解惑了:
object App {
def main(args: Array[String]) {
val addOne = makAdd(1)
val addTwo = makAdd(2)
val t1 = addOne(1)
val t2 = addTwo(2)
}
def makAdd(more: Int) = (x: Int) =&> x + more
}
反編譯生成的class 文件:App$.class
import scala.Function1;
import scala.Serializable;
import scala.runtime.AbstractFunction1.mcII.sp;
public final class App$
{
public static final MODULE$;
private App$()
{
MODULE$ = this;
}
public void main(String[] args)
{
Function1 addOne = makAdd(1);
Function1 addTwo = makAdd(2);
int t1 = addOne.apply$mcII$sp(1);
int t2 = addTwo.apply$mcII$sp(2);
}
public Function1&
public int apply$mcII$sp(int x)
{
return x + this.more$1;
}
public final int apply(int x)
{
return apply$mcII$sp(x);
}
};
}
static
{
new ();
}
}
再看下調試的時候:
在塊中可以參照外部局部變數的方法,並說明塊不只是簡單的代碼,而且把外部「環境」也包括了進來,像這樣的塊稱為閉包。通常的局部變數在方法執行結束時就不存在了,但是如果被包括進了閉包,那麼在閉包存在的期間,局部變數也會一直存在。
以上是Matz對閉包的理解,摘自《松本行弘的程序世界》
簡單地說,就是一個封閉的代碼塊把外界的一些環境包括進來,這就是閉包。
來自我在另一帖子下的評論:
函數A中 聲名一個函數B,函數B被當做參數傳遞給函數C,在函數C內被執行的函數B,因為可以訪問到A內聲明的變數,稱函數B的上下文是一個閉包。 以上,歡迎討論---在補充一下---
可以認為,人們在提到閉包時,指的是回調函數的運用域:js中函數的上下文取決於函數被聲明時的位置,函數C被聲名在A之外,本來不可能訪問A中聲名的變數,而A通過把B當做參數傳給A,或者把B當做返回值定義在 return後面,可以讓外部(比如函數C)訪問到A內的變數。推薦去看王垠的博文http://www.yinwang.org/blog-cn/2012/08/01/interpreter/
可以作為參數傳遞的函數。或者是可以直接call的對象。
推薦閱讀:
※scala相對於python有何優缺點?
※為什麼這兩年函數式編程又火起來了呢?
TAG:JavaScript | Scala | 閉包 |