JavaScript(8)--- 闭包

理解闭包 我的理解是:闭包就是能够读取其他函数内部变量的函数。由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以简单这样理解

"函数A中,有一个函数B,函数B中可以访问函数A中定义的变量或者是数据,此时形成了闭包"。所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁

一、闭包的由来

1、函数嵌套函数

JS之所以会有闭包,是因为JS不同于其他规范的语言,JS允许一个函数中再嵌套子函数,正是因为这种允许函数嵌套,导致JS出现了所谓闭包。

  1. function a(){
  2. var a=1;
  3. function b(){
  4. alert(a);
  5. };
  6. b();
  7. }
  8. a();

在JS正常的函数嵌套中,父函数a调用时,嵌套的子函数b的结构,在内存中产生,然后子函数又接着调用了,子函数b就注销了,此时父函数a也就执行到尾,父函数a也会把

自己函数体内调用时生成的数据从内存都注销。

  1. function a(){
  2. var a=1;
  3. function b(){
  4. alert(a);
  5. }
  6. return b;
  7. }
  8. var f=a();

这个例子中,父函数调用时,函数体内创建了子函数b,但是子函数并没有立即调用,而是返回了函数指针,以备“日后再调用”,因为“准备日后调用”,此时父函数a执行完了

也不敢注销自己的作用域中的数据,因为一旦注销了,子函数b日后再调用时,沿着函数作用域链往上访问数据,就没有数据可以访问了,这就违背了JS函数作用域链的机制。

正因此,子函数要“日后调用”,导致父函数要维持函数作用域链,而不敢注销自己的作用域,那么这个子函数就是“闭包函数”。

2、JS变量的作用域

理解闭包还要理解一个知识点就是 JavaScript的变量作用域。与大多数语言相同,JavaScript变量的作用域分为全局变量和局部变量。**函数内部可以访问全局变量,但是

函数外部无法访问函数内部的局部变量**

示例

  1. function f1(){
  2. let n =100;
  3. var m=99;
  4. console.log(n);
  5. console.log(m);
  6. }
  7. f1(); //输出:100 , 99
  8. console.log(n); //输出:undefined
  9. console.log(m); //输出:undefined

注意 在函数内部声明变量的时候一定要用let或者var。否则,实际上声明了一个全局变量

思考 函数外部如何读取局部变量?

要在函数外部读取局部变量,可以通过在函数内部再定义一个函数的方法来实现。

示例

  1. function f1(){
  2. var n =100;
  3. function f2(){
  4. console.log(n);
  5. }
  6. return f2;
  7. }
  8. let result =f1();
  9. result(); //输出100

在上面的代码中,函数f2就被包括在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。但是反过来就不行,f2内部的局部变量,对f1就是不可见的。这就是Javascript

语言特有的"链式作用域"结构,子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。

二、闭包的作用

闭包可以用在许多地方。它的最大用处有两个 1、可以读取函数内部的变量。2、让变量的值始终保持在内存中。

第一个上面已经实现了,这里就不再重复说明。

1、让变量的值始终保持在内存中

一般来说,全局变量的生存周期是永久的,直到我们主动销毁。而在函数内的局部变量来说,当退出函数时,这些函数变量立即失去它们的价值,也就被垃圾回收机制

销毁了,也算寿终正寝。

示例

  1. //普通的函数
  2. function f1() {
  3. var num = 10;
  4. num++;
  5. return num;
  6. }
  7. console.log(f1()); //11
  8. console.log(f1()); //11
  9. console.log(f1()); //11

可是在闭包中,却不是这样。它可以让这些变量的值使用保持在内存中(不被垃圾回收)

示例

  1. //函数模式的闭包
  2. function f2() {
  3. var num = 10;
  4. function f3() {
  5. num++;
  6. return num;
  7. }
  8. return f3;
  9. }
  10. var ff = f2();
  11. console.log(ff());//11
  12. console.log(ff());//12
  13. console.log(ff());//13

由此可见,当退出函数后,局部变量 num 并没有立即消失,一直存在,这样在第二次调用时 num 才会是在 11的基础上加1,是12,以后每次调用也才会不断加1。

思考 为什么会这样呢?

原因就在于f2是f3的父函数,而f3被赋给了一个全局变量,这导致f3始终在内存中,而f3的存在依赖于f2,因此f2也始终在内存中,不会在调用结束后,被垃圾回收机制

(garbage collection)回收。

三、示例

为了更好理解闭包在实际开发中的应用,这里举几个简单例子来说明闭包。

1、产生三个随机数,但是都是相同的

代码

  1. <script>
  2. //非闭包
  3. function showRandom() {
  4. var num=parseInt(Math.random()*10+1);
  5. console.log(num);
  6. }
  7. showRandom(); //随机
  8. showRandom(); //随机
  9. showRandom(); //随机
  10. console.log("===========================");
  11. //闭包的方式,产生三个随机数,但是都是相同的
  12. function f1() {
  13. //这个只执行一次
  14. var num=parseInt(Math.random()*10+1);
  15. return function () {
  16. console.log(num);
  17. }
  18. }
  19. var ff=f1();
  20. ff(); //这里三个值都是一样的
  21. ff();
  22. ff();
  23. </script>

运行结果

2、点赞示例

先展示运行结果

代码

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>点赞应用</title>
  6. <style>
  7. ul {
  8. list-style-type: none;
  9. }
  10. li {
  11. float: left;
  12. margin-left: 10px;
  13. }
  14. img {
  15. width: 200px;
  16. height: 180px;
  17. }
  18. input {
  19. margin-left: 30%;
  20. }
  21. </style>
  22. </head>
  23. <body>
  24. <ul>
  25. <li><img src="images/ly.jpg" alt=""><br/><input type="button" value="赞(1)"></li>
  26. <li><img src="images/lyml.jpg" alt=""><br/><input type="button" value="赞(1)"></li>
  27. <li><img src="images/fj.jpg" alt=""><br/><input type="button" value="赞(1)"></li>
  28. <li><img src="images/bd.jpg" alt=""><br/><input type="button" value="赞(1)"></li>
  29. </ul>
  30. <script>
  31. //获取所有的按钮
  32. //根据标签名字获取元素
  33. function my$(tagName) {
  34. return document.getElementsByTagName(tagName);
  35. }
  36. //闭包缓存数据
  37. function getValue() {
  38. var value=2;
  39. return function () {
  40. //每一次点击的时候,都应该改变当前点击按钮的value值
  41. this.value="赞("+(value++)+")";
  42. }
  43. }
  44. //获取所有的按钮
  45. var btnObjs=my$("input");
  46. //循环遍历每个按钮,注册点击事件
  47. for(var i=0;i<btnObjs.length;i++){
  48. //注册事件
  49. btnObjs[i].onclick=getValue();
  50. }
  51. </script>
  52. </body>
  53. </html>

参考

1、js闭包的本质

2、JS闭包系列

3、JavaScript闭包

4、js 闭包

  1. 别人骂我胖,我会生气,因为我心里承认了我胖。别人说我矮,我就会觉得好笑,因为我心里知道我不可能矮。这就是我们为什么会对别人的攻击生气。
  2. 攻我盾者,乃我内心之矛(5)。

JavaScript(8)--- 闭包的更多相关文章

  1. 深入理解JavaScript的闭包特性如何给循环中的对象添加事件

    初学者经常碰到的,即获取HTML元素集合,循环给元素添加事件.在事件响应函数中(event handler)获取对应的索引.但每次获取的都是最后一次循环的索引.原因是初学者并未理解JavaScript ...

  2. JavaScript作用域闭包简述

    JavaScript作用域闭包简述 作用域 技术一般水平有限,有什么错的地方,望大家指正. 作用域就是变量起作用的范围.作用域包括全局作用域,函数作用域以块级作用域,ES6中的let和const可以形 ...

  3. JavaScript的闭包原理

    什么是js(JavaScript)的闭包原理,有什么作用? 一.定义 官方解释:闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分. 个人的理解是 ...

  4. Js(javaScript)的闭包原理

    问题?什么是js(javaScript)的闭包原理,有什么作用? 一.定义 官方解释:闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分.  小编 ...

  5. 深入理解javascript的闭包

    闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现. 一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域 ...

  6. 如何给循环中的对象添加事件--深入理解JavaScript的闭包特性

    初学者经常碰到的,即获取HTML元素集合,循环给元素添加事件.在事件响应函数中(event handler)获取对应的索引.但每次获取的都是最后一次循环的索引.原因是初学者并未理解JavaScript ...

  7. javascript,jquery(闭包概念)(转)

    偶尔听人说javascript闭包,让我联想起以前学编译原理和数字逻辑里讲的闭包,以前上课讲的闭包很难懂,而且含有递归的意思在里面,现在不想再查看里面的闭包概念. 但javascript我是经常要用, ...

  8. 理解Javascript 的闭包(closure)

    要理解闭包的概念先从变量的作用域说去 一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之 ...

  9. 两个示例介绍JavaScript的闭包

    JavaScript的闭包有两个用途:一个是访问函数内部的变量:另一个是让变量的值在作用域内保持不变.函数是JavaScript 中唯一有作用域的对象,因此JavaScript的闭包依赖于函数实现,下 ...

  10. javascript 关于闭包的知识点

    javascript 关于闭包的认识 概念:闭包(closure)是函数对象与变量作用域链在某种形式上的关联,是一种对变量的获取机制. 所以要大致搞清三个东西:函数对象(function object ...

随机推荐

  1. 推拿O2O 想说爱你还不容易

    想说爱你还不容易" title="推拿O2O 想说爱你还不容易"> <屌丝男士>第四季最后一集里,乔杉终于圆了"大保健"的梦想,可惜 ...

  2. Linux用户与用户组的关系

    一.用户和用户组文件 1. /etc/passwd:所创建的用户账号和信息均存放在次文件中,所有用户可读取: 最后一个字段的值一般为/sbin/nologin,表示该账号不能用来登陆linux系统: ...

  3. 没有admin权限如何免安装使用Node和NPM

    此教程只针对于在windows系统上没有admin权限和软件安装权限,但是又希望能像安装版一样使用Node和NPM的用户. 步骤一: 下载压缩版node 访问https://nodejs.org/en ...

  4. Google Hacking --你真的会用Google吗?

    你真的会用Google吗?Google Hacking提升工作效率 阅读本文需要6.66分钟 Google hacking,也叫作google dorking.如果在 Google 上搜索 Googl ...

  5. 从0到1,本地到远程git程序过程

    从0到1,本地到远程git程序过程 切记一定要在需要提交代码的文件夹下git init,既是你使用了什么 tortoisegit什么工具,或者你在idea环境下已经add了,但是仍然需要你在当前文件夹 ...

  6. 压力测试(四)-Mysql数据库压测实操

    1.Jmeter压测实战之JDBC request压测Mysql讲解 简介:讲解jdbc压测mysql相关准备工作,jar包添加,配置讲解 1.Thread Group -> add -> ...

  7. php-fpm启动失败处理

    报错信息: No pool defined. at least one pool section must be specified in config file [11-Mar-2019 23:57 ...

  8. 前端H5,点击选择图片控件,图片直接在页面上展示~

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  9. 编程老司机带你玩转 CompletableFuture 异步编程

    本文从实例出发,介绍 CompletableFuture 基本用法.不过讲的再多,不如亲自上手练习一下.所以建议各位小伙伴看完,上机练习一把,快速掌握 CompletableFuture. 个人博文地 ...

  10. 每日一点:git 与 github 区别

    絮絮叨叨在前:以前的公司,都用svn 进行代码管理.最近我那程序猿先生真的受不了我,强迫我使用tortoiseGit. 一开始对于 git 和 github 傻傻分不清,干脆自己整理资料,总结一下. ...