一、闭包的概念

  闭包是JAVASCRIPT中最重要的概念之一,闭包是指有权访问另一个函数作用域中变量的函数;创建闭包常见的方式,就是在一个函数内部,创建另一个函数。以下的例子创建了一个闭包,加粗的两行代码访问了外部函数中的变量propertyName,即使这个内部函数被返回后在其他地方调用,它仍然可以访问这个变量。

function createComparison(propertyName){
    return function(object1, object2){
        var value1 = object1[propertyName];
        var value2 = object2[propertyName];

        if(value1 > value2){
            return 1;  
        }else if(value1 < value2){
            return -1;
        }else{
            return 0;
        }
    };
}

  一般来说,当函数执行完毕后,局部活动对象就会被销毁,内存中只保留全局作用域,但是闭包的情况有所不同。要弄清楚其中的细节,必须彻底弄清楚函数在第一次调用时都发生了什么。

  当某个函数被调用时,会创建一个执行环境以及相应的作用域链,并把作用域链赋值给一个特殊的内部属性[[scope]];然后用this, arguments和其他命名参数的值来初始化函数的活动对象。在作用域链中,外部函数的活动对象处于第二位,外部函数的外部函数的活动对象处于第三位……一直到作为作用域终点的全局执行环境。

  所以,在上面的例子中,当创建compare函数时,会创建一个包含全局变量的作用域链,并保存在内部的[[scope]]属性中;当调用compare函数时,会创建一个执行环境,并复制[[scope]]属性中保存的作用域链。当访问一个变量时,在作用域链中从前往后搜索;一般来说,当函数执行完毕后,局部活动对象就会被销毁,内存中只保留全局作用域,但是对于例子中的闭包,当它返回后,它的作用域链被初始化为包含函数的活动对象和全局变量对象。因此,返回的匿名函数可以返回所有的变量。另外,在createComparision执行完毕后,其活动对象也不会销毁,因为匿名函数的作用域链仍然在引用这个活动对象,直到匿名函数也被销毁,如下例:

// 创建闭包(函数)
var compareNames = createComparison("name");
// 调用闭包(函数)
var result = compareNames({name:"Lillian"},{name:"Matthew"});
// 解除对闭包(函数)的引用,以便安全释放内存
compareNames = null;

  由于闭包会携带包含它的函数的作用域,因此会比其他函数占用更多的内存,因此过度使用闭包可能导致内存占用过多,建议在绝对必要时,再考虑使用闭包。

二、副作用以及解决方法

1、变量

  作用链机制引出了一个值得注意的副作用,即闭包只能取得包含函数中任何一个变量的最后一个值。

function createFunctions(){
    var result = new Array();
    for(var i = 0; i < 10; i++){
        result[i] = function(){
            return i;
        };
    }
    return result;
}
var returnFunctions = createFunctions();
for(var i = 0; i< 10; i++){
    console.log(returnFunctions[i]());
}

// 结果是10个10

  解决方法:不直接赋值给闭包,而是定义匿名函数并立即执行,将匿名函数的结果赋值给数组;这个匿名函数自身也是一个闭包,不过返回的值是变量i创建时的值。

function createFunctions(){
    var result = new Array();
    for(var i = 0; i < 10; i++){
        result[i] = function(num){
            return function(){
                return num;
            };
        }(i);
    }
    return result;
}
var returnFunctions = createFunctions();
for(var i = 0; i< 10; i++){
    console.log(returnFunctions[i]());
}

// 结果是0,1,2,3,4,5,6,7,8,9

2、this

  我们知道,在全局环境中,this等于windows对象,而当函数被作为某个对象的方法调用时,this等于那个对象。但是在闭包中,每个函数被调用时,其活动对象都会自动取得两个特殊的变量,this和arguments。内部函数在搜索这两个变量时,只搜索到活动对象为止,因此永远不可能访问外部函数中的变量。如下例所示:

var name = "The Windows";
var object = {
    name :"my Object",
    getNameFun:function(){
        return function(){
            return this.name;
        };
    }
};
alert(object.getNameFun()());
//"The Windows"

  我们可以通过以下这种方式来避免这个问题:

var name = "The Windows";
var object = {
    name :"my Object",
    getNameFun:function(){
        var that = this;
        return function(){
            return that.name;
        };
    }
};
alert(object.getNameFun()());
//"my Object"

3、内存泄露

  闭包可能会带来内存泄露问题:由于闭包会引用包含函数的整个活动对象,如果闭包的作用域链中保存着一个html对象,那么久意味着该元素无法被销毁(活动对象至少保存着一个引用,通过引用计数来销毁内存的话,引用计数不为0,则无法销毁)。

三、模仿块级作用域

  JAVASCRIPT没有块级作用域的概念,这就意味着在块语句中定义的变量,实际是保存在函数中而非语句中的。

function outputNumbers(count){
    for(var i = 0; i< count; i++){

    }
    alert(i);
    // 不会报错,由于没有块级作用域,for循环结束后,i并不会被销毁
}

  我们函数声明转化成函数表达式,来模仿块级作用域:

(function(){
// 这里是块级作用域
})();

  在一个由很多开发人员共同参与的大型应用程序中,过多的全局变量和函数很容易导致命名冲突。通过这种方式来创建私有作用域,可以很好的避免这个问题:

function outputNumbers(count){
    (function(){
        for(var i = 0; i< count; i++){
    }
    })();

    alert(i);
    // 报错,i没有定义
}

四、函数的定义

  说了这么多,我们再来回顾一下函数的定义。定义函数的方式有两种,一种是函数声明,一种是函数表达式。其中,函数声明的一个重要特征是函数声明提升,这意味着可以把函数声明放在函数调用的后面;而函数表达式没有这种特性。

// 正确
sayHi();
function sayHi(){
    alert("Hi");
}

// 报错
sayHello();
var sayHello = function(){
    alert("helllo");
};

《JAVASCRIPT高级程序设计》闭包的更多相关文章

  1. JavaScript高级程序设计——闭包

    前言 有很多人搞不清匿名函数和闭包这两个概念,经常混用.闭包是指有权访问另一个函数作用域中的变量的函数.匿名函数就是没有实际名字的函数. 闭包 概念 闭包,其实是一种语言特性,它是指的是程序设计语言中 ...

  2. 《JavaScript高级程序设计(第3版)》阅读总结记录第一章之JavaScript简介

    前言: 为什么会想到把<JavaScript 高级程序设计(第 3 版)>总结记录呢,之前写过一篇博客,研究的轮播效果,后来又去看了<JavaScript 高级程序设计(第3版)&g ...

  3. 读javascript高级程序设计00-目录

    javascript高级编程读书笔记系列,也是本砖头书.感觉js是一种很好上手的语言,不过本书细细读来发现了很多之前不了解的细节,受益良多.<br/>本笔记是为了方便日后查阅,仅作学习交流 ...

  4. 读javascript高级程序设计-目录

    javascript高级编程读书笔记系列,也是本砖头书.感觉js是一种很好上手的语言,不过本书细细读来发现了很多之前不了解的细节,受益良多.<br/>本笔记是为了方便日后查阅,仅作学习交流 ...

  5. 读书笔记(03) - 性能 - JavaScript高级程序设计

    作用域链查找 作用域链的查找是逐层向上查找.查找的层次越多,速度越慢.随着硬件性能的提升和浏览器引擎的优化,这个慢我们基本可以忽略. 除了层级查找损耗的问题,变量的修改应只在局部环境进行,尽量避免在局 ...

  6. 《Javascript高级程序设计》阅读记录(七):第七章

    <Javascript高级程序设计>中,2-7章中已经涵盖了大部分精华内容,所以摘录到博客中,方便随时回忆.本系列基本完成,之后的章节,可能看情况进行摘录. 这个系列以往文字地址: < ...

  7. JavaScript高级程序设计第三版.CHM【带实例】

    从驱动全球商业.贸易及管理领域不计其数的复杂应用程序的角度来看,说 JavaScript 已经成为当今世界上最流行的编程语言一点儿都不为过. JavaScript 是一种非常松散的面向对象语言,也是 ...

  8. javascript高级程序设计学习笔记

    javascript高级程序设计,当枕头书已经好久了~zz  现在觉得自己在js的开发上遇到了一些瓶颈,归根究底还是基础太薄弱,所以重新刷一遍js高程希望有更新的认识. 一.javascript简介 ...

  9. JavaScript高级程序设计(读书笔记)之函数表达式

    定义函数的方式有两种:一种是函数声明,另一种就是函数表达式. 函数声明的一个重要特征就是函数声明提升(function declaration hoisting),意思是在执行代码前会先读取函数声明. ...

  10. 《JavaScript高级程序设计(第3版)》笔记-序

    很少看书,不喜欢看书,主要是上学时总坐不住,没有多大定性,一本书可以两天看完,随便翻翻,也可以丢在角落里几个月不去动一下. 上次碰到了<JavaScript高级程序设计(第3版)>感觉真的 ...

随机推荐

  1. Struts2的整体回顾(Action, 拦截器, 值栈, OGNL表示式, ModelDriven)

    ValueStack里有map(request, session, attr, parameters)和对象栈. Map调用的方法: ActionContext.getContext().put(k, ...

  2. HDU 5613 Baby Ming and Binary image

    因为第一行和最后一行都是0,我们只需枚举最左边或最右边一列的01情况,即可得到整张表 然后再检验表是否符合要求 #include<cstdio> #include<cstring&g ...

  3. 用div做下拉列表

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  4. CodeForces 626B Cards

    瞎搞题...凭直觉+猜测写了一发,居然AC了.. #include<cstdio> #include<cstring> #include<cmath> #inclu ...

  5. lpc1788IO口模拟IIC

    #ifndef __MYIIC_H_ #define __MYIIC_H_ #include "common.h" #include "delay.h" #in ...

  6. S3C2440触摸屏驱动详解

    2440的触摸屏转换接口搭载在ADC接口之上,使用上比ADC接口多了一些花样,首先,触摸屏接口有几种转换模式 1. 普通转换模式 单转换模式是最合适的通用ADC转换.此模式可以通过设置ADCCON(A ...

  7. IOS即时通讯XMPP搭建openfire服务器

    一.下载并安装openfire 1.到http://www.igniterealtime.org/downloads/index.jsp下载最新openfire for mac版 比如:Openfir ...

  8. beamer中插入c代码,python代码的经验

    下面是插入的scala代码,它与python在某些语法上类似,所在在https://github.com/olivierverdier/python-latex-highlighting下载了一个py ...

  9. leetcode-004 insertion sort list

    package leetcode; class ListNode { int val; ListNode next; ListNode(int x) { val = x; next = null; } ...

  10. Redis的启动

    http://www.cnblogs.com/goodspeed/archive/2012/10/18/2729615.html http://blog.csdn.net/yulei_qq/artic ...