当前位置: 首页 > news >正文

【代码】js闭包

闭包

  • 闭包的英文是? closure

  • 函数为什么要有闭包特征呢?

    • 如果 A 函数中 声明了 B函数, B函数使用了 A函数作用域的属性

      常规操作: A函数执行结束后,会自动销毁其 函数作用域对象, 导致 x 被销毁, B函数会无法使用

      所以 B函数需要把 A函数的作用域保存在自身的 scopes 属性里

      B.scopes = [ 0: Closure A {x: 10} ]

      即 A函数作用域 是 B函数的闭包

      内层函数, 保存外层函数 作为自己的闭包

  • 利用闭包 我们可以做什么?

    • 防止全局污染

    • 把函数使用的变量 不在全局中声明, 而是在父级函数中声明, 存储在函数作用域

      固定格式:

      var 函数名 = (function(){ 
          var 变量 =return function(){  }
      })()
      
  • 闭包的缺点? 消耗内存

    • 常规: 函数执行完毕后, 会自动销毁开辟的 函数作用域 对象
    • 但是: 因为闭包的存在, 导致对象无法被销毁, 会一直存在于内存中
  • 难以理解??

    • 在群众的呼声中: 2015年出版的 ES6 中, 提供了 let/const 搭配 块级/脚本 两个新生作用域, 比闭包使用更加方便快捷, 用于代替闭包
    • 但是: 你开发的软件为了兼容 2015年之前的浏览器, 例如 IE8, 就无法使用新特性, 只能用闭包
<!DOCTYPE html>
<html lang="en">


<body>
  <!-- 
    函数作用域:函数在 调用时 `临时` 生成的对象, 用于存储函数中生成的变量
    函数作用域 在函数运行完毕后, 会自动销毁 来节省内存
   -->

  <script>
    // 函数对象中有一个属性: scopes -- 作用域们
    // 以 b 函数为例:
    // -- 第一个作用域: a函数作用域 -- 闭包
    // -- 第二个作用域: 全局作用域

    // 总结: 函数运行时会产生临时的函数作用域, 如果这个函数作用域被 子函数保存了, 则称为闭包
    // 具体: a函数作用域, 被 b函数保存在 scopes 属性里, 就说: a函数作用域是b的闭包
    // 为什么: b函数使用了 a 函数作用域中的变量, 假设b函数没有存, 则 a函数执行结束后会自动释放, 导致 b 函数中的 abb 无法使用

    function a() {
      var abb = 'ABB' // 此变量存储在 a函数作用域中

      function b() {
        // 根据上午提到的作用域链: b函数中没有变量 abb, 
        // 所以查找到 上级a函数作用域的abb
        console.log(abb);
      }
      return b
    }

    // b存储的是 a函数的返回值, 即 b 函数
    var b = a() // 调用函数a, 生成一个函数作用域
    b()
    console.dir(b) // dir: 直接输出函数对象, 与log不同

    // b函数使用了 abb 变量, 此变量存储在 a函数的作用域里, a函数执行完毕后, 则会自动释放函数作用域
    // 问: b() 调用时, 还能打印出 abb 变量么??


    var a = 5 //把 5 存放在变量 a 中
    var scopes = {}
    scopes.c = 10 //把10存储在 scopes对象的属性c里

    // 赋值操作 : 动名词结构
    var a = 10 //代表把 10 存储在 变量 a 中

    // a 中存储了 10 : 描述


    // 例如:
    // 涛哥想健身, 去健身房办卡
    // 过了几天后: 健身房倒闭了 -- 办卡无效

    // 闭包:
    // 涛哥想健身, 去健身房买了一套设备, 带回家
    // 健身房倒闭, 也没关系
  </script>
</body>

</html>
<!DOCTYPE html>
<html lang="en">



<body>
  <script>
    // 1. 函数 调用时, 会形成临时的作用域对象, 在函数运行结束后会销毁, 节省内存
    function a() {
      var x = 10

      function b() {
        console.log(x)
      }
      return b
    }

    var b = a() //调用a函数, 生成函数作用域,

    // 函数执行完毕后 变量x 就会销毁
    b() // a()已经执行完毕, 理论上其生成的变量x 会销毁
    // b() 调用时, 还能读取到 x 吗?

    console.dir(b);

    // b函数会把 用到的变量所在的作用域保存在自身的 Scopes 属性里
    // 为了后期 调用时能够正常使用, 不怕销毁

    // a函数作用域销毁了吗?
    // 没有.  以为被b函数保存了

    // JS垃圾回收机制: 没用的东西会自动销毁
    console.log(window);
  </script>
</body>

</html>

闭包应用

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>闭包的应用 14:48</title>
</head>

<body>
  <script>
    // ES6之前的 旧时代, 有哪些作用域?   就两种
    // 全局  + 局部(函数)
    // 自定义变量存储在 全局里, 会造成全局变量污染
    // 那存放在哪里合适??   放局部 -- 即函数作用域

    // 记录函数调用的次数

    // 变量n 专为函数 a 而生, 存储在全局区就不合理 -- 公有的不安全 也 污染全局
    // 所以: 不能放全局作用
    // 就剩下局部作用域能放 -- 函数触发后生成

    // 思路: 利用 匿名函数自调用 快速形成函数作用域
    // (function(){})()  -- 下课去回顾 亮亮的 匿名函数自调用
    var n = 0


    function a() {
      n++
      console.log('n:', n);
    }

    a()
    a()
    a()


    // 声明函数的多种方案:

    // 1. 最普通
    function c() { }

    // 2. 匿名函数赋值给变量
    var c = function () { }

    // 3. 通过触发函数, 返回一个函数
    // d函数触发后, 返回一个函数交给变量 c
    var d = function () {
      // var x = function () { }
      // return x

      return function () { }
    }
    var c = d()


    // 4. 匿名函数自调用格式  (匿名函数)()
    var c = (function () {
      return function () { }
    })()


    // 4. 利用匿名函数 快速完成
    // 改写成匿名函数自调用
    var c = (function () {
      // var: 在哪个作用域中执行, 变量就提升到哪个作用域
      // 在 script 中用var, 就是全局window里
      // 在 函数{} 中用var, 就是函数作用域的
      var n = 0

      return function () {
        n++
        console.log('c的n:', n);
      }
    })()
    // (匿名函数)()

    // 思考: n是记录函数c调用次数的, 他是存在全局吗? 
    c()
    c()
    c()
    c()
    c()
    console.dir(c)

  </script>
</body>

</html>

相关文章:

  • cadence SPB17.4 - allegro - 区域规则设置 - 以smd_pin_to_smd_pin为例
  • 在 Qt 中实现变色的图标(tintColor)
  • MIKE水动力笔记14_数字化海图3之提取任意等深线
  • qml中的一些常用技巧
  • 红黑树,B树、B+树、MySQL索引面试题
  • 基于Vue+Element-ui开发的一个“月日组件”,并发布npm包
  • gRPC RPC技术demo
  • 记录一下ts学习整理的一些知识点
  • java计算机毕业设计基于安卓Android的急救服务APP
  • MyBatis Plus (四) --------- 条件构造器 EntityWrapper
  • 神经网络算法应用案例,神经网络是机器算法吗
  • 2023中国(江西)国际餐饮品牌连锁加盟展览会2月26日开幕
  • Java ServiceLoader、Spring SpringFactoriesLoader、SPI方式解耦第三方组件
  • 聚焦个性化与场景化,全新升级的三星电视看点何在?
  • LeetCode每日一题JAVA、JavaSrcipt题解——2022.08.21-08.31
  • 分享一款快速APP功能测试工具
  • 【Redis学习笔记】2018-06-28 redis命令源码学习1
  • Consul Config 使用Git做版本控制的实现
  • Hibernate最全面试题
  • MySQL QA
  • Python 反序列化安全问题(二)
  • Python学习之路16-使用API
  • Redux 中间件分析
  • thinkphp5.1 easywechat4 微信第三方开放平台
  • vue 配置sass、scss全局变量
  • vue从创建到完整的饿了么(11)组件的使用(svg图标及watch的简单使用)
  • Vue实战(四)登录/注册页的实现
  • Wamp集成环境 添加PHP的新版本
  • 番外篇1:在Windows环境下安装JDK
  • 分享一份非常强势的Android面试题
  • 关于for循环的简单归纳
  • 关于使用markdown的方法(引自CSDN教程)
  • 那些被忽略的 JavaScript 数组方法细节
  • 前端性能优化——回流与重绘
  • 巧用 TypeScript (一)
  • 王永庆:技术创新改变教育未来
  • 微信支付JSAPI,实测!终极方案
  • 想晋级高级工程师只知道表面是不够的!Git内部原理介绍
  • # 计算机视觉入门
  • #HarmonyOS:软件安装window和mac预览Hello World
  • #Linux杂记--将Python3的源码编译为.so文件方法与Linux环境下的交叉编译方法
  • $.ajax,axios,fetch三种ajax请求的区别
  • (2009.11版)《网络管理员考试 考前冲刺预测卷及考点解析》复习重点
  • (4)STL算法之比较
  • (JS基础)String 类型
  • (LNMP) How To Install Linux, nginx, MySQL, PHP
  • (八)c52学习之旅-中断实验
  • (动态规划)5. 最长回文子串 java解决
  • (二)基于wpr_simulation 的Ros机器人运动控制,gazebo仿真
  • (分享)自己整理的一些简单awk实用语句
  • (附源码)计算机毕业设计ssm电影分享网站
  • (附源码)计算机毕业设计ssm基于Internet快递柜管理系统
  • (蓝桥杯每日一题)love
  • (一)VirtualBox安装增强功能
  • (转)ObjectiveC 深浅拷贝学习