前言
文中可能會(huì)有些用刪除線標(biāo)注的內(nèi)容,之所以不刪除是個(gè)人認(rèn)為某種程度上能起一種補(bǔ)充說(shuō)明的作用。
基本概念
scope
Scoping itself is how you search for a variable with a given name.
不只是變量,而應(yīng)該還有函數(shù),所以還是用wikipedia的解釋
In computer programming, the 'scope' of a name binding - an association of a name to an entity, such as a variable – is the region of a computer program where the binding is valid: where the name can be used to refer to the entity.
Scoping這里不太好確定怎么翻譯,因?yàn)槭莻€(gè)現(xiàn)在分詞,百度百科將Lexical Scoping翻譯成詞法域,但是單獨(dú)拿這個(gè)Scoping翻譯成域的話則喪失了現(xiàn)在分詞代表的動(dòng)作正在進(jìn)行的意思了。
A variable has a scope which is the whole area in which that variable can be accessed by name.
變量的范圍是可以通過(guò)名稱訪問(wèn)該變量的整個(gè)區(qū)域。注意這里只是提到了變量,而沒(méi)有說(shuō)到方法。
詞法域(Lexical Scoping)
Scope rules define the visibility rules for names in a programming language.
C/Java/Groovy/Kotlin都是lexically-scoped的語(yǔ)言,而bash就不是一個(gè)lexically-scoped的語(yǔ)言。
一個(gè)塊(block)定義一個(gè)新的域(scope),定義在一個(gè)scope內(nèi)的變量在scope外不可見(jiàn),但是scope外定義的變量在scope內(nèi)可見(jiàn),除非被覆蓋(overriden)掉,C++里有個(gè)概念叫做unqualified-name-lookup.
Lexical scoping也被稱作static scoping,靜態(tài)域或靜態(tài)作用域。
在詞法作用域中,解釋器在本地函數(shù)A(現(xiàn)在運(yùn)行的函數(shù))中搜索,然后在函數(shù)A定義所在的函數(shù)B(或作用域)中搜索,然后在函數(shù)B定義所在的函數(shù)C(或作用域)中搜索。。。 這里的“詞法”是指文本,因?yàn)槟梢酝ㄟ^(guò)查看程序文本中范圍的嵌套來(lái)找出所引用的變量。(這里利用函數(shù)A、B、C來(lái)方便說(shuō)明)
百度百科把Lexical scoping稱作詞法域
動(dòng)態(tài)作用域(Dynamic Scoping)
相反,在動(dòng)態(tài)作用域的機(jī)制中,解釋器首先在本地函數(shù)中搜索,然后在調(diào)用本地函數(shù)的函數(shù)中搜索,然后在調(diào)用該函數(shù)的函數(shù)中搜索,依此回溯調(diào)用堆棧。 “動(dòng)態(tài)”是指變化,因?yàn)槊看握{(diào)用給定函數(shù)時(shí)調(diào)用堆棧都可以不同,因此函數(shù)可能會(huì)根據(jù)調(diào)用它的位置命中不同的變量。
實(shí)現(xiàn)dynamic scoping的最常見(jiàn)的方法有:深綁定(deep binding)和淺綁定(shallow binding)
綁定
深/淺綁定這兩個(gè)名詞只用于當(dāng)一個(gè)過(guò)程(或函數(shù))可以作為一個(gè)參數(shù)傳遞給一個(gè)函數(shù)的時(shí)候。
A(B()) 常規(guī)的嵌套調(diào)用
A(::B) 函數(shù)參數(shù)
深綁定在這個(gè)函數(shù)參數(shù)傳遞的時(shí)候就將其環(huán)境與其綁定。
淺綁定則直到這個(gè)參數(shù)代表的函數(shù)真正執(zhí)行的時(shí)候才將其環(huán)境與其綁定。
幫助理解的話可以看以下代碼在深綁定和淺綁定實(shí)現(xiàn)的時(shí)候,print的結(jié)果:
# https://stackoverflow.com/questions/15550648/shallow-deep-binding-what-would-this-program-print
function f1() {
var x = 10;
function f2(fx)
{
var x;
x = 6;
fx();
};
function f3()
{
print x;
};
f2(f3);
};
深綁定的時(shí)候,會(huì)打印出10
淺綁定的時(shí)候,會(huì)打印出6
什么是Closure
閉包(也稱詞法閉包或函數(shù)閉包)是一種在函數(shù)式語(yǔ)言中實(shí)現(xiàn)詞法范圍的名稱綁定的技術(shù)。在實(shí)現(xiàn)方式上,閉包是對(duì)于函數(shù)與其執(zhí)行環(huán)境的記錄。
閉包 - 不像普通函數(shù) - 允許函數(shù)通過(guò)閉包對(duì)于值或引用(閉包所在的lexical scope中的)進(jìn)行復(fù)制產(chǎn)生的副本來(lái)訪問(wèn)那些捕獲的變量,即使該函數(shù)在其作用域之外被調(diào)用。
Closure is state-full, capture or non-capture.
閉包是有狀態(tài)的,分為捕獲閉包和非捕獲閉包
capture vs non-capture
提到這兩個(gè)詞的時(shí)候需要注意的是,non-capture并不意味著有能不能capture的閉包之分,而是不capture的閉包。
capture-by-value or capture-by-reference
不考慮實(shí)現(xiàn)方式,兩者的區(qū)別類(lèi)似于傳參機(jī)制中的pass-by-value和pass-by-reference。
什么是lambda
Lambda comes from the Lambda Calculus and refers to anonymous functions in programming.
Lambda is stateless
lambda是無(wú)狀態(tài)的
cppreference中是這樣描述lambda expression的:
Constructs a closure: an unnamed function object capable of capturing variables in scope.
lambda表達(dá)式: 構(gòu)造一個(gè)閉包 - 一個(gè)能夠捕獲范圍內(nèi)變量的未命名函數(shù)(匿名函數(shù))
what's lambda for ?
In java , lambda let you express instances of single-method classes more compactly.
lambda讓你在java中能夠更精簡(jiǎn)地實(shí)現(xiàn)一個(gè)只有單個(gè)方法的匿名類(lèi)
兩者有什么區(qū)別
閉包與lambda表達(dá)式的不同之處在于閉包依賴某些變量的詞法域。因此,閉包可以捕獲并攜帶狀態(tài)。雖然lambdas是無(wú)狀態(tài)的,但閉包是有狀態(tài)的。您可以在程序中使用閉包將狀態(tài)從定義的上下文傳送到執(zhí)行點(diǎn)。
語(yǔ)言實(shí)現(xiàn)
Java
因?yàn)樵趈ava中l(wèi)ambda表達(dá)式就是匿名函數(shù),而熟悉匿名函數(shù)的應(yīng)該會(huì)知道,對(duì)于enclosing-scope中的變量需要用final修飾。
java8中得lambda表達(dá)式只能訪問(wèn)enclosing-scope中的final變量,(當(dāng)然普通的全局變量還是可以直接訪問(wèn)的),相對(duì)于舊有的final而言java8中引入了effectively-final的概念,jls中是這樣對(duì)其進(jìn)行定義的
- 聲明中帶有初始化的本地變量在滿足以下條件時(shí)被認(rèn)為是effectively-final
It is not declared final (沒(méi)被final修飾)
It never occurs as the left hand side in an assignment expression (Note that the local variable declarator containing the initializer is not an assignment expression). 從未出現(xiàn)在一個(gè)賦值表達(dá)式的左邊,注意帶初始化的本地變量的聲明語(yǔ)句不是一個(gè)賦值表達(dá)式
It never occurs as the operand of a prefix or postfix increment or decrement operator. 從未出現(xiàn)在自增或自減運(yùn)算符的左邊或者右邊
- 聲明中不進(jìn)行初始化的本地變量在滿足以下情況下被認(rèn)為是effectively-final
It is not declared final (沒(méi)有被final修飾)
Whenever it occurs as the left hand side in an assignment expression, it is definitely unassigned and not definitely assigned before the assignment; that is, it is definitely unassigned and not definitely assigned after the right hand side of the assignment expression. 當(dāng)該變量出現(xiàn)在賦值表達(dá)式的左邊的時(shí)候,該變量在該賦值之前沒(méi)有賦值,而且沒(méi)有進(jìn)行定義賦值;當(dāng)該變量出現(xiàn)在賦值表達(dá)式的右邊的時(shí)候,該變量沒(méi)有被賦值并且在該賦值語(yǔ)句的右邊部分之后也沒(méi)有被定義賦值
It never occurs as the operand of a prefix or postfix increment or decrement operator. 從未出現(xiàn)在自增或者自減運(yùn)算符的左邊或者右邊
一個(gè)方法、構(gòu)造器、lambda或者異常的參數(shù)依照帶初始化的本地聲明變量情況來(lái)判斷是否是effectively-final
C++
有空的時(shí)候再補(bǔ)吧
Kotlin
有空的時(shí)候再補(bǔ)吧
Groovy
有空的時(shí)候再補(bǔ)吧
Python
有空的時(shí)候再補(bǔ)吧
參考
Using closures to capture state
DynamicBinding vs LexicalBinding
java 8 lambda limitations: Closures
The Original 'Lambda Papers' by Guy Steele and Gerald Sussman