理解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 相关文章推荐
深入浅析JavaScript的API设计原则
Jun 14 Javascript
学习Bootstrap滚动监听 附调用方法
Jul 02 Javascript
jQuery插件扩展实例【添加回调函数】
Nov 26 Javascript
vue.js学习笔记之v-bind和v-on解析
May 03 Javascript
看看“疫苗查询”小程序有温度的代码
Jul 31 Javascript
原生JS实现获取及修改CSS样式的方法
Sep 04 Javascript
vue3.0 CLI - 2.1 -  component 组件入门教程
Sep 14 Javascript
vue中render函数的使用详解
Oct 12 Javascript
JavaScript如何获取一个元素的样式信息
Jul 29 Javascript
VUE动态生成word的实现
Jul 26 Javascript
js实现翻牌小游戏
Jul 31 Javascript
Postman无法正常返回结果问题解决
Aug 28 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生成百度sitemap站点地图类函数实例
2014/10/17 PHP
php制作的简单验证码识别代码
2016/01/26 PHP
PHP使用gearman进行异步的邮件或短信发送操作详解
2020/02/27 PHP
jquery获取tr并更改tr内容示例代码
2014/02/13 Javascript
JavaScript对数字的判断与处理实例分析
2015/02/02 Javascript
jQuery插件Elastislide实现响应式的焦点图无缝滚动切换特效
2015/04/12 Javascript
js实现一个链接打开两个链接地址的方法
2015/05/12 Javascript
JavaScript中的slice()方法使用详解
2015/06/06 Javascript
javascript实现用户点击数量统计
2016/12/25 Javascript
js实现随机抽选效果、随机抽选红色球效果
2017/01/13 Javascript
js 实现获取name 相同的页面元素并循环遍历的方法
2017/02/14 Javascript
AngularJs实现聊天列表实时刷新功能
2017/06/15 Javascript
vue proxyTable 接口跨域请求调试的示例
2017/09/12 Javascript
js阻止默认右键的下拉菜单方法
2018/01/02 Javascript
vue移动端路由切换实例分析
2018/05/14 Javascript
node中的cookie的具体使用
2018/09/13 Javascript
详解Nodejs get获取远程服务器接口数据
2019/03/26 NodeJs
Vue 中可以定义组件模版的几种方式
2019/08/06 Javascript
原生js+ajax分页组件
2020/01/30 Javascript
微信小程序实现比较功能的方法汇总(五种方法)
2020/03/07 Javascript
vue中提示$index is not defined错误的解决方式
2020/09/02 Javascript
解决vue-pdf查看pdf文件及打印乱码的问题
2020/11/04 Javascript
[03:17]2014DOTA2 国际邀请赛中国区预选赛 四强专访
2014/05/23 DOTA
使用Python脚本和ADB命令实现卸载App
2017/02/10 Python
python先序遍历二叉树问题
2017/11/10 Python
使用pandas模块读取csv文件和excel表格,并用matplotlib画图的方法
2018/06/22 Python
PYQT5开启多个线程和窗口,多线程与多窗口的交互实例
2019/12/13 Python
python GUI库图形界面开发之PyQt5表格控件QTableView详细使用方法与实例
2020/03/01 Python
python输出结果刷新及进度条的实现操作
2020/07/13 Python
Docker如何部署Python项目的实现详解
2020/10/26 Python
python 基于DDT实现数据驱动测试
2021/02/18 Python
CSS3的文字阴影—text-shadow的使用方法
2012/12/25 HTML / CSS
波兰多品牌运动商店:StreetStyle24.pl
2020/09/22 全球购物
电子商务专业求职信
2014/03/08 职场文书
文明礼仪标语
2014/06/13 职场文书
MySQL实现用逗号进行拼接、以逗号进行分割
2022/12/24 MySQL