浅谈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中:checked选择器用法实例
Jan 04 Javascript
javascript解决IE6下hover问题的方法
Jul 28 Javascript
学习javascript面向对象 掌握创建对象的9种方式
Jan 04 Javascript
ionic隐藏tabs的方法
Aug 29 Javascript
JS禁止查看网页源代码的实现方法
Oct 12 Javascript
解决vue v-for 遍历循环时key值报错的问题
Sep 06 Javascript
VUE+Element UI实现简单的表格行内编辑效果的示例的代码
Oct 31 Javascript
JS/HTML5游戏常用算法之碰撞检测 像素检测算法实例详解
Dec 12 Javascript
微信小程序使用map组件实现获取定位城市天气或者指定城市天气数据功能
Jan 22 Javascript
JS/jQuery实现简单的开关灯效果【案例】
Feb 19 jQuery
JavaScript的Proxy可以做哪些有意思的事儿
Jun 15 Javascript
微信小程序云函数添加数据到数据库的方法
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
php页面缓存ob系列函数介绍
2012/10/18 PHP
php ZipArchive压缩函数详解实例
2013/11/06 PHP
win7系统配置php+Apache+mysql环境的方法
2015/08/21 PHP
jQuery 网易相册鼠标移动显示隐藏效果实现代码
2013/03/31 Javascript
js识别不同浏览器基于userAgent做判断
2014/07/29 Javascript
Javascript核心读书有感之词法结构
2015/02/01 Javascript
JavaScript jquery及AJAX小结
2016/01/24 Javascript
实例详解jQuery的无new构建
2016/08/02 Javascript
js实现四舍五入完全保留两位小数的方法
2016/08/02 Javascript
jQuery使用getJSON方法获取json数据完整示例
2016/09/13 Javascript
基于jQuery实现滚动切换效果
2016/12/02 Javascript
微信小程序实战之运维小项目
2017/01/17 Javascript
Vue.js 中的 $watch使用方法
2017/05/25 Javascript
微信小程序选择图片和放大预览图片功能
2017/11/02 Javascript
代码详解javascript模块加载器
2018/03/04 Javascript
微信小程序自定义prompt组件步骤详解
2018/06/12 Javascript
Vue2.0中三种常用传值方式(父传子、子传父、非父子组件传值)
2018/08/16 Javascript
vue动画之点击按钮往上渐渐显示出来的实例
2018/09/29 Javascript
node.js中express模块创建服务器和http模块客户端发请求
2019/03/06 Javascript
微信小程序页面间跳转传参方式总结
2019/06/13 Javascript
[33:42]LGD vs OG 2018国际邀请赛小组赛BO2 第一场 8.16
2018/08/17 DOTA
Python translator使用实例
2008/09/06 Python
Python使用剪切板的方法
2017/06/06 Python
python-str,list,set间的转换实例
2018/06/27 Python
在pytorch中为Module和Tensor指定GPU的例子
2019/08/19 Python
基于python进行抽样分布描述及实践详解
2019/09/02 Python
Python flask框架实现查询数据库并显示数据
2020/06/04 Python
Python字典实现伪切片功能
2020/10/28 Python
CSS3制作ajax loader icon实现思路及代码
2013/08/25 HTML / CSS
德国综合购物网站:OTTO
2018/11/13 全球购物
贫困生证明范文
2015/06/16 职场文书
《当代神农氏》教学反思
2016/02/23 职场文书
教师节作文之小学四年级
2019/09/03 职场文书
Redis Cluster 字段模糊匹配及删除
2021/05/27 Redis
一次MySQL启动导致的事故实战记录
2021/09/15 MySQL
在python中读取和写入CSV文件详情
2022/06/28 Python