浅谈JavaScript的闭包函数


Posted in Javascript onDecember 08, 2016

 在JavaScript中,闭包恐怕是很多人不能理解的一个概念了,甚至很多人也会把闭包和匿名函数混淆。

 闭包是有权访问另一个函数作用域中的变量的函数。首先要明白的就是,闭包是函数。由于要求它可以访问另一个函数的作用于中的变量,所以我们往往是在一个函数的内部创建另一个函数,而“另一个函数”就是闭包。

 比如之前提到过的作为比较函数:

function createComparisonFunction(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;
    }
   };
  }
</value2){>

 在这个函数中,由于return的函数它访问了包含函数(外部函数)的变量propertyName,所以我们认为这个函数即为闭包。即使这个闭包被返回了,而且是在其他地方调用了,但是它仍然可以访问propertyName,之所以还能够访问到propertyName这个变量,是因为内部函数(闭包)的作用域链中包含着createComparisonFunction函数的作用域。因此,要彻底搞清楚闭包,就需要彻底搞清楚函数被调用时发生了什么以及作用域链的有关知识。

 当某个函数被调用时,会创建一个执行环境(函数一旦被调用,则进入函数执行环境)和相应的作用域链(作用域链是随着执行环境的不同而动态变化的)。(对于函数而言)之后使用arguments其他命名参数的值来初始化函数的活动对象(每个执行环境都有一个变量对象,对于函数成为活动对象)。对于有闭包的函数而言,在作用域链中,外部函数的活动对象始终处于第二位,外部函数的外部函数的活动对象始终处于第三位。。。直至作为作用域链终点的全局执行环境。

 下面撇开闭包不谈,先通过一个简单的例子来理解作用域链以及变量对象和活动对象。

function compare(value1,value2){
 if(value1<value2){
  return -1;
 }else if(value1>value2){
  return 1;
 }else{
  return 0;
 }
}<br>


 var result=compare(5,10);

    以上代码首先定义了compare()函数,然后又在全局作用域中调用了它。当调用compare函数时,首先创建一个函数执行环境,每个执行环境又对应这一个变量对象,也就是说作用域链和函数执行环境是同时创建的,其中作用域链的前端即为compare函数的活动对象(在函数中,变量对象又称为活动对象)。在compare活动对象中包含了arguments、value1、value2(关键:尽管arguments数组对象包含value1和value2,但是我们还是要分开列举,而不是仅仅认为只有arguments包含于compare的活动对象,因为value1和value2也包含于compare的活动对象)。

 对于上述代码而言,全局执行环境的变量对象(再次声明:每一个执行环境都存在相应的变量对象)中包含result和compare,该变量对象在compare()执行环境的作用域链的第二位。

 当我们创建compare()函数时,会创建一个预先包含全局变量对象的作用域链,这个作用域链被保存在compare函数内部的[[scope]]属性中,当调用compare函数时,会为函数创建一个执行环境,然后通过复制函数的[[scope]]属性中的对象构建起执行环境的作用域链。如下:

浅谈JavaScript的闭包函数

  作用域链的本质就是一个指向变量对象的指针列表,它只引用但不实际包含变量对象。无论什么时候在函数中访问一个变量,就会从作用域链的前端沿着作用域链搜索具有相应名字的变量。我们知道,全局环境的变量对象始终存在,而局部环境(如compare()函数执行环境)的变量对象只在函数执行的时候存在,一旦执行完毕,局部变量对象(活动对象)就会被销毁。但在闭包中,却与此不同。

  把博文开始的代码复制如下:

function createComparisonFunction(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;
  }
 };
}

由于在一个函数内部定义的函数会将包含函数(即外部函数)的活动对象添加到它的作用域链中。因此,在createComparisonFunction函数内部定义的匿名函数的作用域中实际包含着外部函数的活动对象。如果我们执行如下代码:

var compare=createComparisonFunction("name");
var result=compare({name:"zzw"},{name:"ht"});

这时候匿名函数的作用域链将引用着外部函数的活动对象。因为匿名函数从外部函数中被返回后,它的作用域链被初始化为包含外部函数的活动对象和全局变量对象。这样,匿名函数就可以访问外部函数中定义的所有变量。更为重要的是,即使外部函数在执行完毕后,其活动对象也不会被销毁,因为匿名函数的作用域链仍然在引用这个活动对象。换句话说,当createComparison()函数返回后,其执行环境的作用域链会被销毁,但是她的活动对象仍然保存在内存中。等到倪敏函数被销毁后,外部函数的活动对象才会被销毁。

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

模仿块级作用域

(function(){
 var now=new Date();
 if(now.getMonth()==0&&now.getDate()==1){
  alert("happy new year");
 }
})();

这就是模仿块级作用域,即定义并立即调用了一个匿名函数。

如下为演示其作用:

function outputNumbers(count){
 (function(){
  for (var i=0;i<count;i++){
   console.log(i);
  }
 })();
 console.log(i);
}
outputNumbers(5);

这是在模仿块级作用域之外的console.log(i)就会导致错误,因为i未被定义。说明在执行了模仿块级作用域之后,内部的变量就被销毁了。

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,同时也希望多多支持三水点靠木!

Javascript 相关文章推荐
读jQuery之十 事件模块概述
Jun 27 Javascript
Android中资源文件(非代码部分)的使用概览
Dec 18 Javascript
jQuery通过ajax请求php遍历json数组到table中的代码(推荐)
Jun 12 Javascript
JavaScript创建对象的七种方式(推荐)
Jun 26 Javascript
从零开始学习搭建React脚手架项目
Aug 23 Javascript
Vue中props的详解
May 16 Javascript
Vue使用Clipboard.JS在h5页面中复制内容实例详解
Sep 03 Javascript
js消除图片小游戏代码
Dec 11 Javascript
微信小程序实现同时上传多张图片
Feb 03 Javascript
微信小程序scroll-view点击项自动居中效果的实现
Mar 25 Javascript
element-ui 弹窗组件封装的步骤
Jan 22 Javascript
Ajax实现局部刷新的方法实例
Mar 31 Javascript
node.js Sequelize实现单实例字段或批量自增、自减
Dec 08 #Javascript
Vue.JS入门教程之自定义指令
Dec 08 #Javascript
微信公众号开发 自定义菜单跳转页面并获取用户信息实例详解
Dec 08 #Javascript
node.js学习之交互式解释器REPL详解
Dec 08 #Javascript
JavaScript正则表达式小结(test|match|search|replace|split|exec)
Dec 08 #Javascript
JS中用childNodes获取子元素换行会产生一个子元素
Dec 08 #Javascript
微信公众号 摇一摇周边功能开发
Dec 08 #Javascript
You might like
PHP 服务器配置(使用Apache及IIS两种方法)
2009/06/01 PHP
php文件上传你必须知道的几点
2015/10/20 PHP
php 无限级分类 获取顶级分类ID
2016/03/13 PHP
PHP如何防止XSS攻击与XSS攻击原理的讲解
2019/03/22 PHP
DOM 脚本编程中的兄弟节点
2009/10/31 Javascript
javaScript矢量图表库-gRaphael几行代码实现精美的条形图/饼图/点图/曲线图
2013/01/09 Javascript
JS连连看源码完美注释版(推荐)
2013/12/09 Javascript
聊一聊jQuery插件uploadify使用方法
2016/08/24 Javascript
javascript简单进制转换实现方法
2016/11/24 Javascript
JavaScript实现的冒泡排序法及统计相邻数交换次数示例
2017/04/26 Javascript
微信小程序中button组件的边框设置的实例详解
2017/09/27 Javascript
详解使用vue-admin-template的优化历程
2018/05/20 Javascript
深入Vue-Router路由嵌套理解
2018/08/13 Javascript
利用node 判断打开的是文件 还是 文件夹的实例
2019/06/10 Javascript
微信小程序实现3D轮播图效果(非swiper组件)
2019/09/21 Javascript
js实现多个标题吸顶效果
2020/01/08 Javascript
jQuery实现可以扩展的日历
2020/12/01 jQuery
python 实现插入排序算法
2012/06/05 Python
spyder常用快捷键(分享)
2017/07/19 Python
疯狂上涨的Python 开发者应从2.x还是3.x着手?
2017/11/16 Python
Python图形绘制操作之正弦曲线实现方法分析
2017/12/25 Python
Python装饰器语法糖
2019/01/02 Python
windows+vscode安装paddleOCR运行环境的步骤
2020/11/11 Python
韩国邮政旗下生鲜食品网上超市:epost
2016/08/27 全球购物
美国克罗格超市在线购物:Kroger
2019/06/21 全球购物
Dyson戴森波兰官网:Dyson.pl
2019/08/05 全球购物
Set里的元素是不能重复的,那么用什么方法来区分重复与否呢? 是用==还是equals()? 它们有何区别?
2014/07/27 面试题
文员个人求职自荐信
2013/09/21 职场文书
银行服务感言
2014/03/01 职场文书
宣传普通话标语
2014/06/27 职场文书
2014年有孩子的离婚协议书范本
2014/10/08 职场文书
2014年学校禁毒工作总结
2014/12/23 职场文书
经典搞笑版检讨书
2015/02/19 职场文书
敬老院志愿者活动总结
2015/05/06 职场文书
完美解决golang go get私有仓库的问题
2021/05/05 Golang
python入门学习关于for else的特殊特性讲解
2021/11/20 Python