浅谈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 相关文章推荐
用javascript实现在小方框中浏览大图的代码
Aug 14 Javascript
javascript:;与javascript:void(0)使用介绍
Jun 05 Javascript
javascript实现简单的贪吃蛇游戏
Mar 31 Javascript
jquery实现用户打分评分特效
May 28 Javascript
JS拖动鼠标画出方框实现鼠标选区的方法
Aug 05 Javascript
jquery密码强度校验
Dec 02 Javascript
Vue.js表单控件实践
Oct 27 Javascript
基于vue2框架的机器人自动回复mini-project实例代码
Jun 13 Javascript
Angular.js前台传list数组由后台spring MVC接收数组示例代码
Jul 31 Javascript
聊聊JS动画库 Velocity.js的使用
Mar 13 Javascript
webpack 从指定入口文件中提取公共文件的方法
Nov 13 Javascript
ES10 特性的完整指南小结
Mar 04 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
Dedecms V3.1 生成HTML速度的优化办法
2007/03/18 PHP
PHP实现CSV文件的导入和导出类
2015/03/24 PHP
php在linux环境中如何使用redis详解
2020/12/15 PHP
javascript测试题练习代码
2012/10/10 Javascript
node.js中的fs.close方法使用说明
2014/12/17 Javascript
javascript+html5实现绘制圆环的方法
2015/07/28 Javascript
js日期相关函数dateAdd,dateDiff,dateFormat等介绍
2016/09/24 Javascript
详解angularjs 关于ui-router分层使用
2017/06/12 Javascript
Node接收电子邮件的实例代码
2017/07/21 Javascript
JS闭包的几种常见形式实例详解
2017/09/16 Javascript
Three.js加载外部模型的教程详解
2017/11/10 Javascript
jQuery实现基本动画效果的方法详解
2018/09/06 jQuery
简单的Python抓taobao图片爬虫
2014/10/26 Python
Python输出带颜色的字符串实例
2017/10/10 Python
python将一个英文语句以单词为单位逆序排放的方法
2018/12/20 Python
Flask教程之重定向与错误处理实例分析
2019/08/01 Python
Python TKinter如何自动关闭主窗口
2020/02/26 Python
Python3.9 beta2版本发布了,看看这7个新的PEP都是什么
2020/06/10 Python
pytorch 限制GPU使用效率详解(计算效率)
2020/06/27 Python
Python调用高德API实现批量地址转经纬度并写入表格的功能
2021/01/12 Python
如何用python写个模板引擎
2021/01/14 Python
Hunter Boots美国官方网站:赫特威灵顿雨靴
2018/06/16 全球购物
即时搜索数百万张门票:SeatsForEveryone.com
2018/08/26 全球购物
Yahoo-PHP面试题3
2012/01/14 面试题
事业单位辞职信范文
2014/01/19 职场文书
道路建设实施方案
2014/03/18 职场文书
新春寄语大全
2014/04/09 职场文书
暑期培训班策划方案
2014/08/26 职场文书
KTV门卫岗位职责
2014/10/09 职场文书
2016春节慰问信范文
2015/03/25 职场文书
2015年办公室文秘工作总结
2015/04/30 职场文书
房屋产权证明书
2015/06/19 职场文书
百年孤独读书笔记
2015/06/29 职场文书
MySQL 常见的数据表设计误区汇总
2021/06/07 MySQL
《总之就是很可爱》新作短篇动画《总之就是很可爱~制服~》将于2022年夏天播出
2022/04/07 日漫
带你了解Java中的ForkJoin
2022/04/28 Java/Android