什么是闭包

维基百科中的概念

  • 在计算机科学中,闭包(也称词法闭包或函数闭包)是指一个函数或函数的引用,与一个引用环境绑定在一起,这个引用环境是一个存储该函数每个非局部变量(也叫自由变量)的表。
  • 闭包,不同于一般的函数,它允许一个函数在立即词法作用域外调用时,仍可访问非本地变量

学术上

  • 闭包是指在 JavaScript 中,内部函数总是可以访问其所在的外部函数中声明的参数和变量,即使在其外部函数被返回return掉(寿命终结)了之后。

个人理解

  • 闭包是在函数里面定义一个函数,该函数可以是匿名函数,该子函数能够读写父函数的局部变量。

闭包的常见案例分析

案例分析是从浅入深希望大家都看完!

  • 案例1---基本介绍:
function A(){var localVal=10;return localVal;
}

A();//输出30

复制代码

function A(){ var localVal=10; return function(){ console.log(localVal); return localVal; } } var func=A(); func();//输出10 复制代码

两段代码,在第二段代码中,函数A内的匿名函数可以访问到函数A中的局部变量这就是闭包的基本使用。

  • 案例2---前端实现点击事件
!function(){var localData="localData here";document.addEventListener('click',function(){console.log(localData);    });
}();
复制代码

前端原始点击事件操作也用到了闭包来访问外部的局部变量。

  • 案例3---ajax请求
!function(){var localData="localData here";var url="http://www.baidu.com";$.ajax({url:url,success:function(){//do sth...console.log(localData);}})
}();
复制代码

在ajax请求的方法中也用到了闭包,访问外部的局部变量。

  • 案例4---for循环案例
var arrays = [];

for (var i=0; i<3; i++) { arrays.push(function() { console.log('>>> ' + i); //all are 3 }); }

复制代码

复制代码

上面的这段代码,刚看了代码一定会以为陆续打印出1,2,3,实际输出的是3,3,3,出现这种情况的原因是匿名函数保存的是引用,当for循环结束的时候,i已经变成3了,所以打印的时候变成3。出现这种情况的解决办法是利用闭包解决问题。

for (var i=0; i<3; i++) {(function(n) {tasks.push(function() {console.log('>>> ' + n);});})(i);
}
复制代码

闭包里的匿名函数,读取变量的顺序,先读取本地变量,再读取父函数的局部变量,如果找不到到全局里面搜索,i作为局部变量存到闭包里面,所以调整后的代码可以能正常打印1,2,3。

闭包与内存泄漏

  • javascript回收后内存的方式:

javascript的主要通过计数器方式回收内存,假设有a,b,c三个对象,当a引用b的时候,那么b的引用计算器增加1(通俗的说用到那个对象哪个对象引用计算器增加1),同时b引用c的时候,c引用计数器增加1,当a被释放的时候,b的引用计数器减少1,变成0的时候这个对象被释放,c计数器变成0,被释放,但是当遇到b和c之间互相引用的时候,无法通过计数器方式释放内存。

  • 闭包可以导致上面所说b和c互相引用无法释放内存 第一个案例的代码拿过来分析:
function A(){var localVal=10;return function(){console.log(localVal);return localVal;}
}
var func=A();
func();//输出10
复制代码

当A函数结束的时候,想要释放,发现它的localVal变量被匿名函数引用,所有A函数无法释放,导致内存泄漏。但是也有好处,闭包正是可以做到这一点,因为它不会释放外部的引用,从而函数内部的值可以得以保留。

说明:闭包不代表一定会带来内存泄漏,良好的使用闭包是不会造成内存泄漏的。

闭包的应用

  • 封装
var person = function(){    //变量作用域为函数内部,外部无法访问    var name = "default";
<span class="hljs-keyword" style="line-height: 26px; color: #569CD6;">return</span> {    <span class="hljs-attr" style="line-height: 26px; color: #9CDCFE;">getName</span> : <span class="hljs-function" style="line-height: 26px; color: #DCDCDC;"><span class="hljs-keyword" style="line-height: 26px; color: #569CD6;">function</span>(<span class="hljs-params" style="line-height: 26px; color: #DCDCDC;"></span>)</span>{    <span class="hljs-keyword" style="line-height: 26px; color: #569CD6;">return</span> name;    },    <span class="hljs-attr" style="line-height: 26px; color: #9CDCFE;">setName</span> : <span class="hljs-function" style="line-height: 26px; color: #DCDCDC;"><span class="hljs-keyword" style="line-height: 26px; color: #569CD6;">function</span>(<span class="hljs-params" style="line-height: 26px; color: #DCDCDC;">newName</span>)</span>{    name = newName;    }
}
复制代码

}();

print(person.name);//直接访问,结果为undefined print(person.getName());
person.setName("kaola");
print(person.getName());

得到结果如下:

复制代码

undefined
default
kaola 复制代码

  • 实例中的for循环另一种形式
doucument.body.innerHTML="<div id=div1>aaa</div>"+"<div id=div2>bbb</div>"+"<div id=div3>ccc</div>";
for(var i=1;i<4;i++){!function(i){document.getElementById('div'+i);addEventListener('click',function(){alert(i);//1,2,3 });}
}
复制代码
  • 结果缓存
var CachedSearchBox = (function(){    var cache = {},    count = [];    return {    attachSearchBox : function(dsid){    if(dsid in cache){//如果结果在缓存中    return cache[dsid];//直接返回缓存中的对象    }    var fsb = new uikit.webctrl.SearchBox(dsid);//新建    cache[dsid] = fsb;//更新缓存    if(count.length > 100){//保正缓存的大小<=100    delete cache[count.shift()];    }    return fsb;          },
   <span class="hljs-attr" style="line-height: 26px; color: #9CDCFE;">clearSearchBox</span> : <span class="hljs-function" style="line-height: 26px; color: #DCDCDC;"><span class="hljs-keyword" style="line-height: 26px; color: #569CD6;">function</span>(<span class="hljs-params" style="line-height: 26px; color: #DCDCDC;">dsid</span>)</span>{    <span class="hljs-keyword" style="line-height: 26px; color: #569CD6;">if</span>(dsid <span class="hljs-keyword" style="line-height: 26px; color: #569CD6;">in</span> cache){    cache[dsid].clearSelection();      }    }
};
复制代码

})();

复制代码

CachedSearchBox.attachSearchBox("input"); 复制代码

说明:开发中会碰到很多情况,设想我们有一个处理过程很耗时的函数对象,每次调用都会花费很长时间,那么我们就需要将计算出来的值存储起来,当调用这个函数的时候,首先在缓存中查找,如果找不到,则进行计算,然后更新缓存并返回值,如果找到了,直接返回查找到的值即可。闭包正是可以做到这一点,因为它不会释放外部的引用,从而函数内部的值可以得以保留。

面试题分析

闭包测试题: 求输出结果

function fun(n,o){console.log(o);return {fun:function(m){//[2]return fun(m,n);//[1]}}
}
复制代码

var a=fun(0); a.fun(1); a.fun(2); a.fun(3); var b=fun(0).fun(1).fun(2).fun(3); var c=fun(0).fun(1); c.fun(2); c.fun(3); 复制代码

由于分析内容比较多,大家可直接参考这篇文章 https://cnodejs.org/topic/567ed16eaacb6923221de48f

分析内容说明,在看这篇文章的时候,注意两点可能会看的更明白:

  • JS的词法作用域,JS变量作用域存在于函数体中即函数体,并且变量的作用域是在函数定义声明的时候就是确定的,而非在函数运行时。
  • 在JS中调用函数的时候,如果用一个参数的方法调用两个参数的方法,这时候只是第二个参数未定义,代码不会报错停止运行,正常流程往下走,像面试题中仍然会返回一个对象。

总结

  1. 闭包其实是在函数内部定义一个函数。
  2. 闭包在使用的时候不会释放外部的引用,闭包函数内部的值会得到保留。
  3. 闭包里面的匿名函数,读取变量的顺序,先读取本地变量,再读取父函数的局部变量。
  4. 对于闭包外部无法引用它内部的变量,因此在函数内部创建的变量执行完后会立刻释放资源,不污染全局对象。
  5. 闭包使用的时候要考虑到内存泄漏,因为不释放外部引用,但是合理的使用闭包是内存使用不是内存泄漏。

觉得本文对你有帮助?请分享给更多人

欢迎大家关注我的公众号——程序员成长指北。请自行微信搜索——“程序员成长指北”

javascript中的闭包这一篇就够了相关推荐

  1. 让你分分钟学会Javascript中的闭包

    Javascript中的闭包 前面的话: 闭包,是 javascript 中重要的一个概念,对于初学者来讲,闭包是一个特别抽象的概念,特别是ECMA规范给的定义,如果没有实战经验,你很难从定义去理解它 ...

  2. 解析面试常问题之JavaScript中的闭包概念及应用,顺便普及一下大家口中常说的内存泄漏问题

    JavaScript中的闭包是一个面试中经常被考到的问题,大家可能都对这个概念多多少少都有一些模糊的概念或者一点都不了解,那么今天就来给大家讲解一下. 公众号:前端印象 不定时有送书活动,记得关注~ ...

  3. 一篇文章把你带入到JavaScript中的闭包与高级函数

    在JavaScript中,函数是一等公民.JavaScript是一门面向对象的编程语言,但是同时也有很多函数式编程的特性,如Lambda表达式,闭包,高阶函数等,函数式编程时一种编程范式. funct ...

  4. [译]Javascript中的闭包(closures)

    本文翻译youtube上的up主kudvenkat的javascript tutorial播放单 源地址在此: https://www.youtube.com/watch?v=PMsVM7rjupU& ...

  5. 【javascript笔记】关于javascript中的闭包

    最开始看<javascript高级程序设计>的时候就看到了javascript中的闭包,在第七章第二节....好大概知道了,过了段时间,好了又忘了... 我们来看这本书里面关于闭包是怎么描 ...

  6. 深入理解JavaScript中的闭包

    闭包没有想象的那么简单 闭包的概念在JavaScript中占据了十分重要的地位,有不少开发者分不清匿名函数和闭包的概念,把它们混为一谈,我希望借这篇文章能够让大家对闭包有一个清晰的认识. 大家都知道变 ...

  7. javascript中的闭包closure详解

    文章目录 简介 函数中的函数 Closure闭包 使用闭包实现private方法 闭包的Scope Chain 闭包常见的问题 闭包性能的问题 总结 简介 闭包closure是javascript中一 ...

  8. 闭包的示例_用示例解释JavaScript中的闭包

    闭包的示例 什么是封包? (What are Closures?) A closure is the combination of a function and the lexical environ ...

  9. JavaScript中的闭包原理

    关于闭包 函数可以通过作用域链互相关联起来,函数体内部的变量都可以保存在函数作用域内,这种特性被称之为闭包 闭包的原理 函数内部的变量函数外部是无法获取的,如果我们要访问函数内部的某个变量或是变量值的 ...

最新文章

  1. ASP3.0给我们带来的新技术之一---DataShaping技术
  2. universal image loader在listview/gridview中滚动时重复加载图片的问题及解决方法
  3. 导出数据库数据成txt格式
  4. vue.js初识(一)
  5. php ci rest,在CodeIgniter框架中使用RESTful服务
  6. [杭电ACM]1012u Calculate e
  7. 成为高效程序员的几大搜索技巧
  8. 『飞秋』WCF热门问题编程示例
  9. Spring实战(六)自动装配的歧义性
  10. 万年历c语言编程怎么做,用C语言如何编写“万年历”
  11. javascript中闭包的真正作用
  12. 数据结构—平衡二叉树
  13. 几款web富文本编辑器汇总整理
  14. 游戏算法整理(贴图完整版)
  15. Icode编程>>>图形化编程>>>1级训练场>>>基础训练【1】
  16. gre填空高频词汇整理
  17. CSS3之颜色渐变效果
  18. 蜀门一直显示连接服务器,蜀门进不去点进入之后,出现无法连接服务器 – 手机爱问...
  19. Cairo图形指南(6)
  20. 新版微信页面底部导航问题

热门文章

  1. html canvas text 居中,HTML5 Canvas Text文本居中实例
  2. c语言课件 文件,C语言课件--文件.ppt
  3. python卸载opencv_怎么为python安装新版的opencv模块-百度经验
  4. android将矩阵转换成字节数组,android-使用OpenGL矩阵转换将纹理从“ 1D”映...
  5. html5盒子模型作业,html5 盒子模型案例
  6. python 内置方法赋值_Python内置函数
  7. app息屏后ajax请求不执行_息屏时钟app下载-息屏时钟软件下载v1.0 安卓版
  8. java红黑树_JAVA学习-红黑树详解
  9. docker 启动petalinux镜像脚本
  10. 概率论与数理统计专业术语