Javascript 闭包详解及实例代码


Posted in Javascript onNovember 30, 2016

Javascript 闭包

闭包,是 Javascript 比较重要的一个概念,对于初学者来讲,闭包是一个特别抽象的概念,特别是ECMA规范给的定义,如果没有实战经验,很难从定义去理解它。因此,本文不会对闭包的概念进行大篇幅描述,直接上干货,让你分分钟学会闭包!

1 闭包,一睹为快

在接触一个新技术的时候,我首先会做的一件事就是找它的 demo code。对于我们来说,看代码比自然语言更能理解一个事物的本质。其实,闭包无处不在,比如:jQuery、zepto的核心代码都包含在一个大的闭包中,所以下面我先写一个最简单最原始的闭包,以便让你在大脑里产生闭包的画面:

function A(){
  function B(){
    console.log("Hello Closure!");
  }
  return B;
}
var C = A();
C();//Hello Closure!

这是最简单的闭包。

有了初步认识后,我们简单分析一下它和普通函数有什么不同,上面代码翻译成自然语言如下:

(1)定义普通函数 A

(2)在 A 中定义普通函数 B

(3)在 A 中返回 B

(4)执行 A, 并把 A 的返回结果赋值给变量 C

(5)执行 C

把这5步操作总结成一句话就是:

函数A的内部函数B被函数A外的一个变量 c 引用。

把这句话再加工一下就变成了闭包的定义:

当一个内部函数被其外部函数之外的变量引用时,就形成了一个闭包。

因此,当你执行上述5步操作时,就已经定义了一个闭包!

这就是闭包。

2 闭包的用途

在了解闭包的作用之前,我们先了解一下 Javascript 中的GC机制:

在 Javascript 中,如果一个对象不再被引用,那么这个对象就会被 GC 回收,否则这个对象一直会保存在内存中。

在上述例子中,B 定义在 A 中,因此 B 依赖于 A ,而外部变量 C 又引用了 B , 所以A间接的被 C 引用。

也就是说,A 不会被 GC 回收,会一直保存在内存中。为了证明我们的推理,上面的例子稍作改进:

function A(){
  var count = 0;
  function B(){
    count ++;
    console.log(count);
  }
  return B;
}
var C = A();
C();// 1
C();// 2
C();// 3

count 是函数A 中的一个变量,它的值在函数B 中被改变,函数B 每执行一次,count 的值就在原来的基础上累加 1 。因此,函数A中的 count 变量会一直保存在内存中。

当我们需要在模块中定义一些变量,并希望这些变量一直保存在内存中但又不会“污染”全局的变量时,就可以用闭包来定义这个模块。

 3 高端写法

上面的写法其实是最原始的写法,而在实际应用中,会将闭包和匿名函数联系在一起使用。下面就是一个闭包常用的写法:

(function(document){
  var viewport;
  var obj = {
    init:function(id){
      viewport = document.querySelector("#"+id);
    },
    addChild:function(child){
      viewport.appendChild(child);
    },
    removeChild:function(child){
      viewport.removeChild(child);
    }
  }
  window.jView = obj;
})(document);

这个组件的作用是:初始化一个容器,然后可以给这个容器添加子容器,也可以移除一个容器。

功能很简单,但这里涉及到了另外一个概念:立即执行函数。 简单了解一下就行,需要重点理解的是这种写法是如何实现闭包功能的。

可以将上面的代码拆分成两部分:(function(){}) 和 () , 第1个() 是一个表达式,而这个表达式本身是一个匿名函数,所以在这个表达式后面加 () 就表示执行这个匿名函数。

因此这段代码执行执行过程可以分解如下:

var f = function(document){
  var viewport;
  var obj = {
    init:function(id){
      viewport = document.querySelector("#"+id);
    },
    addChild:function(child){
      viewport.appendChild(child);
    },
    removeChild:function(child){
      viewport.removeChild(child);
    }
  }
  window.jView = obj;
};
f(document);

在这段代码中似乎看到了闭包的影子,但 f 中没有任何返回值,似乎不具备闭包的条件,注意这句代码:

window.jView = obj;

obj 是在函数 f 中定义的一个对象,这个对象中定义了一系列方法, 执行window.jView = obj 就是在 window 全局对象定义了一个变量 jView,并将这个变量指向 obj 对象,即全局变量 jView 引用了 obj . 而 obj 对象中的函数又引用了函数 f 中的变量 viewport ,因此函数 f 中的 viewport 不会被 GC 回收,viewport 会一直保存到内存中,所以这种写法满足了闭包的条件。

 4 简单的总结语

这是对闭包最简单的理解,当然闭包还有其更深层次的理解,这个就涉及的多了,你需要了解JS的执行环境(execution context)、活动对象(activation object)以及作用域(scope)和作用域链(scope chain)的运行机制。但作为初学者,暂时不必了解这些,有了简单的理解之后,一定要在实际项目中用起来,等你用的多了,对于闭包,你自然会有更深层次的理解!

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

Javascript 相关文章推荐
用jscript实现新建和保存一个word文档
Jun 15 Javascript
jquery 图片上传按比例预览插件集合
May 28 Javascript
fancybox modal的完美解决(右上的X)
Oct 30 Javascript
javascript的解析执行顺序在各个浏览器中的不同
Mar 17 Javascript
javascript Deferred和递归次数限制实例
Oct 21 Javascript
node.js中的path.sep方法使用说明
Dec 08 Javascript
JS实现模拟百度搜索“2012世界末日”网页地震撕裂效果代码
Oct 31 Javascript
微信小程序 wxapp内容组件 progress详细介绍
Oct 31 Javascript
jquery实现一个全局计时器(商城可用)
Jun 30 jQuery
浅析node Async异步处理模块用例分析及常用方法介绍
Nov 17 Javascript
vue实现鼠标经过动画
Oct 16 Javascript
详解JavaScript修改注册表的方法
Jan 05 Javascript
jQuery特殊符号转义的实现
Nov 30 #Javascript
Javascript 引擎工作机制详解
Nov 30 #Javascript
将JSON字符串转换成Map对象的方法
Nov 30 #Javascript
JS实现重新加载当前页面或者父页面的几种方法
Nov 30 #Javascript
JS实现重新加载当前页面
Nov 29 #Javascript
jQuery弹出div层过2秒自动消失
Nov 29 #Javascript
headjs实现网站并行加载但顺序执行JS
Nov 29 #Javascript
You might like
php学习笔记 数组的常用函数
2011/06/13 PHP
PHP数字前补0的自带函数sprintf 和number_format的用法(详解)
2017/02/06 PHP
Thinkphp5.0 框架视图view的比较标签用法分析
2019/10/12 PHP
让whoops帮我们告别ThinkPHP6的异常页面
2020/03/02 PHP
关于__defineGetter__ 和__defineSetter__的说明
2007/05/12 Javascript
简单的Jquery全选功能
2013/11/07 Javascript
jquery获取复选框checkbox的值实现方法
2016/05/30 Javascript
AngularJS创建自定义指令的方法详解
2016/11/03 Javascript
js图片加载效果实例代码(延迟加载+瀑布流加载)
2017/05/12 Javascript
bootstrap table插件的分页与checkbox使用详解
2017/07/23 Javascript
基于Vue实例生命周期(全面解析)
2017/08/16 Javascript
vue toggle做一个点击切换class(实例讲解)
2018/03/13 Javascript
浅谈手写node可读流之流动模式
2018/06/01 Javascript
基于vue开发微信小程序mpvue-docs跳转页面功能
2019/04/10 Javascript
Vue快速实现通用表单验证功能
2019/12/05 Javascript
[47:22]Mineski vs Winstrike 2018国际邀请赛小组赛BO2 第二场 8.16
2018/08/17 DOTA
利用Python中的pandas库对cdn日志进行分析详解
2017/03/07 Python
python利用urllib和urllib2访问http的GET/POST详解
2017/09/27 Python
pygame实现俄罗斯方块游戏
2018/06/26 Python
关于Python 常用获取元素 Driver 总结
2019/11/24 Python
Python使用pickle进行序列化和反序列化的示例代码
2020/09/22 Python
Python类绑定方法及非绑定方法实例解析
2020/10/09 Python
全网最详细的PyCharm+Anaconda的安装过程图解
2021/01/25 Python
HTML5实现自带进度条和滑块滑杆效果
2018/04/17 HTML / CSS
美国性感内衣店:Yandy
2018/06/12 全球购物
C语言笔试集
2012/07/24 面试题
医学生自我评价
2014/01/27 职场文书
两只小狮子教学反思
2014/02/05 职场文书
保护环境倡议书范文
2014/05/13 职场文书
银行职员工作失误检讨书
2014/10/14 职场文书
农村党员学习党的群众路线教育实践活动心得体会
2014/11/04 职场文书
应届生简历自我评价
2015/03/11 职场文书
2016年9月份红领巾广播稿
2015/12/21 职场文书
素质教育培训心得体会
2016/01/19 职场文书
ObjectMapper 如何忽略字段大小写
2021/06/29 Java/Android
Pygame游戏开发之太空射击实战敌人精灵篇
2022/08/05 Python