我正在阅读有关面向对象JavaScript的内容,发现了闭包的概念。我并不太理解它何时以及为什么要使用。其他像Java这样的语言是否也具有闭包?基本上,我想了解如何通过了解闭包的概念来帮助我改进编码。
我正在阅读有关面向对象JavaScript的内容,发现了闭包的概念。我并不太理解它何时以及为什么要使用。其他像Java这样的语言是否也具有闭包?基本上,我想了解如何通过了解闭包的概念来帮助我改进编码。
import java.util.Arrays;
import java.util.Comparator;
public class StupidComparator {
public static void main(String[] args) {
// this is a value used (bound) by the inner class
// note that it needs to be "final"
final int numberToCompareTo=10;
// this is an inner class that acts like a closure and uses one bound value
Comparator<Integer> comp=new Comparator<Integer>() {
public int compare(Integer a, Integer b) {
int result=0;
if (a<numberToCompareTo) result=result-1;
if (b<numberToCompareTo) result=result+1;
return result;
}
};
Integer[] array=new Integer[] {1,10, 5 , 15, 6 , 20, 21, 3, 7};
// this is a function call that takes the inner class "closure" as a parameter
Arrays.sort(array,comp);
for (int i:array) System.out.println(i);
}
}
在不同语言中,闭包有着不同的名称,但其本质为以下几点:
要创建闭包,需要使用支持将函数类型绑定到变量并像字符串、整数或布尔值一样传递它的编程语言。
还需要能够内联声明函数。在JavaScript中,可以这样实现:
foo("bar", "baz" function(x){alert("x")});
我们可以将匿名函数作为参数传递给foo函数。我们可以使用这种方法来创建闭包。
闭包可以"捕获"变量,因此可以用于传递范围内的变量。考虑下面的示例:
function foo(){
var spam = " and eggs";
return function(food){alert(food + spam)};
}
var sideOfEggs = foo();
现在,eggs的一侧包含一个函数,它将“ and eggs”附加到传递给它的任何食物。 spam变量是foo函数作用域的一部分,并且本应该在函数退出时丢失,但是闭包"闭合"了命名空间,只要闭包保持在内存中,就会保留它。
所以,我们清楚了闭包可以访问其父级私有作用域变量,对吗?那么如何使用它们来模拟JavaScript中的私有访问修饰符?
var module = (function() {
var constant = "I can not be changed";
return {
getConstant : function() { //This is the closure
return constant; //We're exposing an otherwise hidden variable here
}
};
}()); //note the function is being defined then called straight away
module.getConstant(); //returns "I can not be changed"
module.constant = "I change you!";
module.getConstant(); //still returns "I can not be changed"
这里发生的是我们正在创建并立即调用一个匿名函数。函数中有一个私有变量。它返回一个具有单个方法的对象,该方法引用此变量。一旦函数退出,getConstant方法就是访问变量的唯一方式。即使删除或替换此方法,也不会泄露其秘密。我们使用闭包实现了封装和变量隐藏。有关更详细的说明,请参见http://javascript.crockford.com/private.html
Java目前还没有闭包的概念。最接近的是匿名内部类。但是要以内联方式实例化其中之一,您必须实例化整个对象(通常来自现有接口)。闭包的美在于它们封装简单、表达性强的语句,而这在匿名内部类的噪音中有些遗失。
constant
变量的作用域是正确的,但我认为module.constant
与函数内的var constant
不同。您所做的是将另一个常量添加到模块作为属性。您可以通过再次阅读module.constant
来验证这一点,它仍将返回“我改变了你!” - AaA虽然Java没有一流函数(first-class functions),但实际上它确实具有词法闭包(lexical closures)。
例如,下面这个来自Paul Graham的书《On Lisp》的Lisp函数返回一个加数字的函数:
(defun make-adder (n)
(lambda (x) (+ x n))
这可以在Java中完成。然而,由于它没有一级函数,我们需要定义一个接口(让我们称其为Adder)和一个匿名内部类,其中包含实现此接口的函数。
public interface Adder {
int add(int x);
}
public static Adder makeAdder(final int n) {
return new Adder() {
public int add(int x) {
return x + n;
}
};
}
内部的add()函数是一个词法闭包,因为它使用了外部词法作用域中的n变量。
为了实现这一点,该变量必须声明为final,这意味着该变量不能更改。但是,即使它们是final,也可以更改引用变量中的值。例如,考虑以下来自On Lisp的Lisp函数:
(defun make-adderb (n)
(lambda (x &optional change)
(if change
(setq n x)
(+ n n))))
可以通过将外部变量封装在一个引用类型变量中(例如数组或对象)来在Java中实现此操作。
public interface MutableAdder {
int add(int x, boolean change);
}
public static MutableAdder makeAdderB(int n) {
final int[] intHolder = new int[] { n };
return new MutableAdder() {
public int add(int x, boolean change) {
if (change) {
intHolder[0] = x;
return x;
}
else {
return intHolder[0] + x;
}
}
};
}
我会声称这是真正的词法闭包,而不是模拟。但我不会声称它很漂亮。
闭包是一种作用域技术。Java没有闭包。
在JavaScript中,您可以像以下示例那样实现:
var scope = this;
var f = function() {
scope.somethingOnScope //scope is 'closed in'
}
闭包是一种非常自然的功能,它允许自由变量被其词法环境所捕获。
以下是javascript中的一个示例:
function x() {
var y = "apple";
return (function() {
return y;
});
}
函数x返回一个函数。请注意,当创建函数时,不会像返回表达式时那样计算在此函数中使用的变量。函数被创建时,它会查看哪些变量不是函数本地变量(自由变量)。然后,它定位这些自由变量并确保它们不会被垃圾回收,以便在实际调用函数时可以使用。
为了支持此功能,您需要具有一级函数,而Java不支持此功能。
请注意,这是在JavaScript等语言中拥有私有变量的一种方式。