理解Javascript闭包


Posted in Javascript onNovember 01, 2013

闭包是ECMAScript一个很重要的特征,但是却很难用合适的定义来描述它。虽然闭包很难清晰地描述,但是,却很容易创建,或者说,不小心创建。然而,闭包的存在其实是有一定的潜在问题的。为了避免“不小心”地创建闭包,以及更好地利用闭包的优点,有必要理解闭包的机制。

闭包的定义
 
关于闭包,有太多的定义,特别是有一些定义非常抽象,象这个:

A "closure" is an expression (typically a function) that can have free variables together with an environment that binds those variables.

大致是说闭包是一个表达式,拥有一些自由变量及绑定这些变量的执行环境。这种定义太书面化,反而难以理解。

还有另一个定义:
所有函数都是闭包。这个定义给我很大的迷惑,换句话说,由于Javascript没有块级作用域,因此闭包一般指的是函数(想不出除了函数以外还有哪些方式可以构成闭包)。

这里不想太多讨论函数与闭包的关系,下面给出我认为比较容易理解的定义吧。

首先,闭包的存在是基于作用域链。由于作用域链的机制,所有函数(即使全局函数)都能引用上下文执行环境中的变量(即free variables)。

其次,闭包内部必须有free variables。顺便说下两种变量1. Local variables (bound variables) 2. Non-local variables (free variables)

最后,在其上下文环境结束后仍然存在。即内部函数拥有比它的外部函数更长的生命周期。

 
关于闭包定义的解析
 
关于闭包定义的两点,一直在考虑是不是必须同时满足。

首先,如果闭包内部没有free variables,即是说它没有访问外部的变量,那么就失去了闭包的意义。(除非通过其他闭包改变了行为)因此,我认为free variables是必要条件。

其次,如果函数内部存在free variables,但是当其上下文环境销毁后,它也跟着销毁。可以想象内部函数,虽然访问了其外部函数变量,但是当外部函数执行完后也随之回收。这种情况下,闭包的讨论也没有意义。

 
来看两个例子:

var objectA = (function() {
        var localA = "localA";        innerFn();
              // 单纯的内部函数调用
        function innerFn() {
            localA = "innerChange";
        }
        return {
            getLocalA : function() {
                return "empty";
            }
        };
    })();
    objectA.getLocalA();
    objectA.getLocalA = function() {
        return localA;
    };
    //console.log(objectA.getLocalA()); //error: localA is not defined
 
    var objectB = (function() {
        var localB = "localB";
        return {
            getLocalB : function() {
                return "empty";
            },
            updateGetLocalB : function() {
                this.getLocalB = function() {
                    return localB;
                };
            },
            updateLocalB : function() {
                localB = "changeLocalB";
            }
        };
    })();
    console.log(objectB.getLocalB()); // empty
       // 通过其他闭包改变
    objectB.updateGetLocalB();
    console.log(objectB.getLocalB()); // localB
    objectB.updateLocalB();
    console.log(objectB.getLocalB()); // changeLocalB

闭包的优点和缺点

闭包的优点是闭包内部可以访问到定义它们的外部函数的参数和变量(除了this和arguments)。
闭包主要的问题便是它会保存包含它的函数的作用域,因此比一般函数占用更多的内存空间,因此不宜过度使用闭包。

闭包的应用

闭包最基本的应用场景便是通过保护内部变量从而实现私有,比如模块模式。

Javascript 相关文章推荐
半角全角相互转换的js函数
Oct 16 Javascript
Extjs4 类的定义和扩展实例
Jun 28 Javascript
javascript按位非运算符的使用方法
Nov 14 Javascript
手机平板等移动端适配跳转URL的js代码
Jan 25 Javascript
JS+CSS实现可拖动的弹出提示框
Feb 16 Javascript
Javascript this 函数深入详解
Dec 13 Javascript
js 获取今天以及过去日期
Apr 11 Javascript
微信小程序之数据双向绑定与数据操作
May 12 Javascript
angularjs实现分页和搜索功能
Jan 03 Javascript
Vue数据双向绑定的深入探究
Nov 27 Javascript
Vue两种组件类型:递归组件和动态组件的用法
Aug 06 Javascript
JavaScript的Set数据结构详解
Feb 18 Javascript
Javascript 命名空间模式
Nov 01 #Javascript
完美解决AJAX跨域问题
Nov 01 #Javascript
javascript中创建对象的几种方法总结
Nov 01 #Javascript
如何学习Javascript入门指导
Nov 01 #Javascript
js动态设置鼠标事件示例代码
Oct 30 #Javascript
获取非最后一列td值并将title设为该值的方法
Oct 30 #Javascript
eclipse如何忽略js文件报错(附图)
Oct 30 #Javascript
You might like
PHP Ajax实现页面无刷新发表评论
2007/01/02 PHP
php cli模式学习(PHP命令行模式)
2013/06/03 PHP
PHP中判断变量为空的几种方法分享
2013/08/26 PHP
PHP实现的DES加密解密实例代码
2016/04/06 PHP
php微信开发之带参数二维码的使用
2016/08/03 PHP
php和html的区别点详细总结
2019/09/24 PHP
Laravel 对某一列进行筛选然后求和sum()的例子
2019/10/10 PHP
Jquery cookie操作代码
2010/03/14 Javascript
Extjs中常用表单介绍与应用
2010/06/07 Javascript
js简单实现删除记录时的提示效果
2013/12/05 Javascript
Javascript遍历table中的元素示例代码
2014/07/08 Javascript
探讨js字符串数组拼接的性能问题
2014/10/11 Javascript
使用prop解决一个checkbox选中后再次选中失效的问题
2017/07/05 Javascript
利用types增强vscode中js代码提示功能详解
2017/07/07 Javascript
webpack+vue-cil中proxyTable处理跨域的方法
2018/07/20 Javascript
详解如何制作并发布一个vue的组件的npm包
2018/11/10 Javascript
[57:53]Secret vs Pain 2018国际邀请赛小组赛BO2 第二场 8.17
2018/08/20 DOTA
浅析python 内置字符串处理函数的使用方法
2014/06/11 Python
python进阶教程之动态类型详解
2014/08/30 Python
Python 专题六 局部变量、全局变量global、导入模块变量
2017/03/20 Python
Python3使用SMTP发送带附件邮件
2020/06/16 Python
pandas把所有大于0的数设置为1的方法
2019/01/26 Python
Pandas_cum累积计算和rolling滚动计算的用法详解
2019/07/04 Python
pytorch 获取层权重,对特定层注入hook, 提取中间层输出的方法
2019/08/17 Python
pandas按行按列遍历Dataframe的几种方式
2019/10/23 Python
opencv python Canny边缘提取实现过程解析
2020/02/03 Python
Vision Direct比利时:在线订购隐形眼镜
2019/08/27 全球购物
美国知名的隐形眼镜电商:Contacts America
2019/11/19 全球购物
静态成员和非静态成员的区别
2012/05/12 面试题
奥巴马演讲稿
2014/01/08 职场文书
委托公证书
2014/04/08 职场文书
英文感谢信范文
2015/01/21 职场文书
2015年打非治违工作总结
2015/04/02 职场文书
小学运动会加油稿
2015/07/22 职场文书
解决springboot druid数据库连接失败后一直重连的方法
2022/04/19 Java/Android
win10音频服务未响应怎么解决?win10音频服务未响应未修复的解决方法
2022/08/14 数码科技