从理论角度讨论JavaScript闭包(4)

从理论角度:所有的函数。因为它们都在创建的时候就将上层上下文的数据保存起来了。哪怕是简单的全局变量也是如此,因为函数中访问全局变量就相当于是在访问自由变量,这个时候使用最外层的作用域。

从实践角度:以下函数才算是闭包:

♦即使创建它的上下文已经销毁,它仍然存在(比如,内部函数从父函数中返回)

♦在代码中引用了自由变量

闭包实践

实际使用的时候,闭包可以创建出非常优雅的设计,允许对funArg上定义的多种计算方式进行定制。 如下就是数组排序的例子,它接受一个排序条件函数作为参数:

[1, 2, 3].sort(function (a, b) {
        ... // 排序条件
    });

同样的例子还有,数组的map方法(并非所有的实现都支持数组map方法,SpiderMonkey从1.6版本开始有支持),该方法根据函数中定义的条件将原数组映射到一个新的数组中:  

[1, 2, 3].map(function (element) {
        return element * 2;
    }); // [2, 4, 6]

使用函数式参数,可以很方便的实现一个搜索方法,并且可以支持无穷多的搜索条件:  

someCollection.find(function (element) {
        return element.someProperty == 'searchCondition';
    });

还有应用函数,比如常见的forEach方法,将funArg应用到每个数组元素:   

[1, 2, 3].forEach(function (element) {
        if (element % 2 != 0) {
            alert(element);
        }
    }); // 1, 3

顺便提下,函数对象的 apply 和 call方法,在函数式编程中也可以用作应用函数。这里,我们将它们看作是应用函数 —— 应用到参数中的函数(在apply中是参数列表,在call中是独立的参数): 

(function () {
        alert([].join.call(arguments, ';')); // 1;2;3
    }).apply(this, [1, 2, 3]);

闭包还有另外一个非常重要的应用 —— 延迟调用:   

var a = 10;
    setTimeout(function () {
        alert(a); // 10, 一秒钟后
    }, 1000);

也可以用于回调函数:

... var x = 10; // only for example xmlHttpRequestObject.onreadystatechange = function () { // 当数据就绪的时候,才会调用; // 这里,不论是在哪个上下文中创建,变量“x”的值已经存在了 alert(x); // 10 }; ..

还可以用于封装作用域来隐藏辅助对象:

var foo = {}; // initialization (function (object) { var x = 10; object.getX = function _getX() { return x; }; })(foo); alert(foo.getX()); // get closured "x" – 10

几个闭包样例

例子1:闭包中局部变量是引用而非拷贝

function say667() { // Local variable that ends up within closure var num = 666; var sayAlert = function() { alert(num); } num++; return sayAlert; } var sayAlert = say667(); sayAlert()

因此执行结果应该弹出的667而非666。

例子2:多个函数绑定同一个闭包,因为他们定义在同一个函数内。

function setupSomeGlobals() { // Local variable that ends up within closure var num = 666; // Store some references to functions as global variables gAlertNumber = function() { alert(num); } gIncreaseNumber = function() { num++; } gSetNumber = function(x) { num = x; } } setupSomeGlobals(); // 为三个全局变量赋值 gAlertNumber(); //666 gIncreaseNumber(); gAlertNumber(); // 667 gSetNumber(12); // gAlertNumber(); //12

 例子3:当在一个循环中赋值函数时,这些函数将绑定同样的闭包

function buildList(list) { var result = []; for (var i = 0; i < list.length; i++) { var item = 'item' + list[i]; result.push( function() {alert(item + ' ' + list[i])} ); } return result; } function testList() { var fnList = buildList([1,2,3]); // using j only to help prevent confusion - could use i for (var j = 0; j < fnList.length; j++) { fnList[j](); } }

testList的执行结果是弹出item3 undefined窗口三次,因为这三个函数绑定了同一个闭包,而且item的值为最后计算的结果,但是当i跳出循环时i值为4,所以list[4]的结果为undefined.(再具体原因,前面有解释过。)

例子4:外部函数所有局部变量都在闭包内,即使这个变量声明在内部函数定义之后。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/ab426502a907340373799f20c09067eb.html