浅谈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压缩混淆工具
May 16 Javascript
基于jquery的合并table相同单元格的插件(精简版)
Apr 05 Javascript
JS获取屏幕,浏览器窗口大小,网页高度宽度(实现代码)
Dec 17 Javascript
jQuery简单实现提交数据出现loading进度条的方法
Mar 29 Javascript
jQuery实现日期联动效果实例
Jul 26 Javascript
Bootstrap 模态框(Modal)带参数传值实例
Aug 20 Javascript
jquery animate动画持续运动的实例
Nov 29 jQuery
Bootbox将后台JSON数据填充Form表单的实例代码
Sep 10 Javascript
vue watch关于对象内的属性监听
Apr 22 Javascript
JavaScript封闭函数及常用内置对象示例
May 13 Javascript
基于vue 实现表单中password输入的显示与隐藏功能
Jul 19 Javascript
详解Vue-cli3.X使用px2rem遇到的问题
Aug 09 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制作静态网站的模板框架
2006/10/09 PHP
找到了一篇jQuery与Prototype并存的冲突的解决方法
2007/08/29 Javascript
JS面向对象编程浅析
2011/08/28 Javascript
运行Node.js的IIS扩展iisnode安装配置笔记
2015/03/02 Javascript
JavaScript数组迭代器实例分析
2015/06/09 Javascript
javascript检测移动设备横竖屏
2016/05/21 Javascript
详解angular2采用自定义指令(Directive)方式加载jquery插件
2017/02/09 Javascript
微信小程序获取循环元素id以及wx.login登录操作
2017/08/17 Javascript
JavaScript中Require调用js的实例分享
2017/10/27 Javascript
微信小程序支付及退款流程详解
2017/11/30 Javascript
解决vue热替换失效的根本原因
2018/09/19 Javascript
点击按钮弹出模态框的一系列操作代码实例
2019/03/29 Javascript
Vue项目中配置pug解析支持
2019/05/10 Javascript
ES6 Map结构的应用实例分析
2019/06/26 Javascript
Python获取当前时间的方法
2014/01/14 Python
Python 基础教程之包和类的用法
2017/02/23 Python
Python对切片命名的实现方法
2018/10/16 Python
python实现简易动态时钟
2018/11/19 Python
使用Python 统计高频字数的方法
2019/01/31 Python
解决python3中的requests解析中文页面出现乱码问题
2019/04/19 Python
Python Flask 搭建微信小程序后台详解
2019/05/06 Python
Python爬虫实现验证码登录代码实例
2019/05/10 Python
python安装requests库的实例代码
2019/06/25 Python
Topshop美国官网:英国快速时尚品牌
2019/05/16 全球购物
struct与class的区别
2014/02/03 面试题
应届生高等护理求职信
2013/10/12 职场文书
优秀高中生事迹材料
2014/02/11 职场文书
歌唱比赛主持词
2014/03/18 职场文书
合作意向书格式及范文
2014/03/31 职场文书
合作投资意向书
2014/04/01 职场文书
施工工地安全标语
2014/06/07 职场文书
社会实践活动总结范文
2014/07/03 职场文书
2015年中秋放假通知范文
2015/08/18 职场文书
2016优秀青年志愿者事迹材料
2016/02/25 职场文书
Python机器学习之KNN近邻算法
2021/05/14 Python
利用Python读取微信朋友圈的多种方法总结
2021/08/23 Python