JavaScript全解析-闭包

news2024/11/20 8:45:44

闭包

重新认识函数
●一个函数分为函数的定义和函数的执行
函数的定义
●我们书写一个函数, 但是不会执行函数体内的代码
●那定义一个函数做了什么事情
○在堆内存中开辟一段存储空间
○把你书写在函数体内的代码, 全部以字符串的形式存储在这存储空间中, 此时不会解析变量
○把函数存储空间地址, 赋值给变量名(函数名)
函数的执行
●使用(调用)函数, 会把函数体内的代码执行
●调用函数又具体做了什么事情
○按照变量名存储的地址, 找到函数存储空间
○直接开辟一个新的函数执行空间
○在执行空间内进行形参赋值
○在执行空间内进行函数内部的预解析
○把之前存储的代码在执行空间内完整执行一遍, 此时才会解析变量
○执行完毕这个开辟出来的执行空间销毁

let num = 100
function fn(a, b) {
  // 证明确实没有解析 num 存储
  // 如果这里在存储的时候, 是解析了 num 存储的, 那么存起来的就是 100
  //   后面的 num = 200 这句代码不会影响到这里的值
  //   将来打印出来就是 100
  // 如果这里在存储的时候, 没有解析 num 存储, 那么存起来的就是 num 变量
  //   后面的 num = 200 这句代码会影响到这里的值
  //   将来打印出来就是 200
  // console.log(num)
  const res = a + b
  console.log(res)
}
num = 200
fn(10, 20)
fn(100, 200)


复制代码

函数形参赋值和与解析先后问题

function fn(a) {
  console.log(a)
  function a() { console.log(111) }
}
fn(100)


复制代码

分析
●当函数调用的时候, 如果先进行的 形参赋值
○1、给 a 变量赋值为 100
○2、进行函数内与解析的时候, 给 a 变量赋值为一个 函数体
■此时给 a 变量赋值的函数体就会把 100 覆盖
○3、执行的第一行代码, 打印 a 变量
■在这里就会打印出 函数体
●当函数调用的时候, 如果先进行的 预解析
○1、进行函数内预解析的时候, 给 a 变量赋值为一个 函数体
○2、进行形参赋值的时候, 给 a 变量赋值为 100
■此时给 a 变量赋值的 100 就会把函数体覆盖
○3、执行的第一行代码, 打印 a 变量
■在这里就会打印出 100
●结论
○如果控制台打印出 100, 说明 先 预解析 后 形参赋值
○如果控制台打印出 函数体, 说明 先 形参赋值 后 预解析
不会销毁的函数执行空间
●当你的函数内返回一个 复杂数据类型
●并且在函数外面有变量接受这个复杂数据类型的时候
●此时函数的执行空间不会销毁
不会销毁的函数执行空间有什么作用
●延长了变量的生命周期
如何能让这个空间销毁
●让你的外部变量指向别的位置, 只要不在保存这个内部地址
●这个执行空间就会被 浏览器 销毁掉

function fn() {
  const num = 100
  const obj = { name: 'Jack' }
  return obj
}
let res = fn()
const res2 = fn()
// 销毁了执行空间
res = 200


复制代码

闭包
定义
●官方定义:就是能够读取其他函数内部变量的函数
●个人理解:
○需要一个不会销毁的函数执行空间
○函数内 直接 或者 间接 的返回一个函数
○内部函数使用着外部函数的私有变量
○我们称内部函数是外部函数的闭包函数
作用(优点)
●延长变量的生命周期
●在函数外部操作函数内部的私有变量
1.直接返回一个函数

function outer() {
  var num = 100
  function inner() {
    return num
  }
  return inner
}
const res = outer()
// 当你需要在 outer 外面访问 outer 内的私有变量 num 的时候
// 没有办法直接访问
// 但是你可以通过调用 res 来访问
// 因为 res 存储的就是 inner 函数地址
// 所以这里就是在调用 outer 内的 inner 函数
// 又因为 inner 内没有私有变量 num, 会自动去到 父级作用域查找
// 也就是去到 outer 内查找私有变量 num , 发现有
// 索引返回的就是 outer 函数内的 私有变量 num
// n 得到的就是 outer 内私有变量 num 的值
const n = res()


复制代码

2.间接返回一个函数
●不直接返回函数, 把函数放在一个容器内返回

function outer() {
  var num = 100
  var str = 'hello world'
  // 如果我向对象内放置一个函数, 那么返回的虽然是对象, 但是对象内有函数
  var obj = {
    getNum: function () { return num },
    setNum: function (val) {
      // 给 num 变量进行赋值
      // 自己作用域内有, 给自己的赋值, 自己没有给父级的赋值
      // 因为 setNum 内没有 num 变量
      // 所以, 这里的赋值实际上是给 outer 函数内的私有变量 num 赋值
      num = val
    }
  }
  return obj
}
// res 接受的就是 outer 函数内返回的 obj 对象的地址
// res 和 outer 函数内的 obj 指向一个对象存储空间
var res = outer()
// 当你调用 res.getNum() 的时候, 就是在调用 outer 函数内的 obj 里面的 getNum 函数
// 因为 getNum 函数是 outer 的子级作用域
// 所以 getNum 内没有 num 的时候, 访问的还是 outer 函数内的私有变量 num
var n = res.getNum()
console.log(n)
// 因为 res 就是 outer 函数内的 obj 对象
// 所以, res.setNum 就是 obj 内的 setNum 函数
// 调用了 obj 内的 setNum 函数, 并且给 val 赋值为了 500
// 因为 setNum 函数内是在给 outer 函数的私有变量 num 赋值
// 所以这里的 500 被赋值给了 outer 函数的私有变量 num
// 在 outer 函数外面给 outer 函数内部的 私有变量 num 的值修改了
res.setNum(500)
console.log(res.getNum())


复制代码

沙箱模式
●沙箱:
○就是一个箱子里面装的都是沙子
○筛子会一点儿一点儿的漏出来
○不会一下子都漏出来
●利用 函数内 间接返回一个函数
○间接就是不直接
○把函数包裹在另一个数据结构中返回
○一般是 一个函数内返回一个对象 , 对象中书写多个函数
○意义就是为了返回多个函数

function outer() {
    let a = 100
    let b = 200
    let str = 'hello world'
    // 创建一个对象
    const obj = {
        // 书写一个函数, 目的是为了获取私有变量 a
        getA: function () { return a },
        getB () { return b },
        setA: function (val) { a = val }
    }
    return obj
}
// 目的是为了得到 "箱子"
const res = outer()
        
// 利用 "箱子" 去访问 outer 函数内的指定数据
// 需要访问 a
console.log(res.getA()) // 100
console.log(res.getB()) // 200
// 需要修改 a
res.setA(1000)
console.log(res.getA()) // 1000
// 重新做了一个 "箱子"
const res2 = outer()
console.log(res2.getA()) // 100


复制代码

沙箱模式语法糖
●尽可能的简化沙箱模式的语法
●利用的是 getter 和 setter 来进行操作数据
●所有的框架的底层使用的大部分是这样的操作

<script>
         /*
            沙箱语法糖
                + 尽可能的简化沙箱模式的语法
                + 利用的是 getter 和 setter 来进行操作数据
        */

    function outer() {
      let a = 100
      let b = 200

      // 准备对象的时候, 按照 getter 获取器 和 setter 设置器的方式来设置
      const obj = {
        // getA 目的为了获取 a 成员, 使用 getter 获取器
        get a () { return a },
        // getB 目的为了获取 b 成员, 使用 getter 获取器
        get b () { return b },
        // setA 目的是为了设置 a 成员, 使用 setter 设置器
        set a (val) { a = val },
        set b (val) { b = val }
      }

      return obj
    }

    // res 得到的是一个 对象数据类型
    const res = outer()

    // 因为闭包的形式, 导致我们的所有操作
    // 对象.方法名()

    console.log(res)
    // console.log(res.a)

    // 在对象内, 直接给设置器名称赋值即可
    // res.a = 1000
    // console.log(res.a)
</script>


复制代码

案例-循环绑定事件

<!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>Document</title>
</head>
<body>
  <button>1</button><button>2</button><button>3</button><button>4</button><button>5</button>

  <script>
    // 在没有let出现之前

    const btns = document.querySelectorAll('button')
    // 循环获取到每一个button按钮
    for (var i = 0; i < btns.length; i++) {
        // 给每一个按钮添加一个点击事件
        // 使用的是一个自执行函数
        btns[i].onclick = (function (index) {
            // 返回一个函数
            return function () {
            // 使用传递进来的 i
            console.log(index)
            }
        })(i) // i当做实参传递到函数里面

    }
  </script>
</body>
</html>


复制代码

函数柯理化
●最简单的柯理化函数就是把
●一次传递两个参数 , 变成两次每次传递一个参数
●利用了闭包 , 把第一次传递的参数保存下来(延长变量的生命周期)


// 需求: 求 10 + x 的和
// 之前的方式
function sum(x) {
    return 10 + x
}
// 使用
const res = sum(10)
console.log(res);
// 需求: 求 20 + x 的和
function sum1(x) {
    return 20 + x
}
// 使用
const res1 = sum1(10)
console.log(res1);


复制代码

●柯理化函数实现


// 我们利用闭包(柯理化)实现
function curry(init) {
    return function (num) {
        return num + init
    }
}
// 使用
// 求 10 + x 的和
// const sum10 = curry(10);
// console.log(sum10(20));
// 代码优化
console.log(curry(10)(20));
// 求 20 + x 的和
// const sum20 = curry(20)
// console.log(sum20(20));
// 代码优化
console.log(curry(20)(20));


复制代码

函数柯理化封装
●通过我们上面使用的柯理化
●我们发现柯理化有两个功能
○一个是用来保存变量(收集变量)
○一个是用来实现逻辑(功能函数)
●我们柯理化函数的封装有可能是多个参数在相互利用
柯理化函数封装
●把一个功能函数按照 柯理化的方式 封装起来
●让这个功能函数变成一种 柯理化的 调用方式

// 准备功能函数
function fn(a, b, c, d) {
    return a + '://' + b + ':' + c + d
}
// 准备柯理化函数
function currying(fn, ...arg) {
// ...arg 把除了功能函数以外的参数全部收集了
// console.log('功能函数 : ', fn)
// console.log('收集的基础参数 : ', arg)
// 保存外部 arg 变量
let _arg = arg || []
// 记录: 功能函数需要几个参数
// 语法: 函数名.length
// 得到: 该函数的 形参的个数
const len = fn.length
// 返回一个内部函数
return function (...arg) {
    // 这个函数将来被调用的时候, 依旧需要接受参数
    // 把本次调用的时候接受的实参, 和之前准备好的拼接在一起
    _arg = [ ..._arg, ...arg ]
    // 目前 _arg 就是基础准备的参数 和 本次调用时候传递的参数 放在一起的 数组
    if (_arg.length < len) {
        // 再次调用 currying 函数, 继续去收集参数
        return currying(fn, ..._arg)
    } else {
    // 执行功能函数
        return fn(..._arg)
    }
}
}
// 将来使用的时候
// const r1 = currying(fn, 'http', 'localhost', '8080')
// const r2 = currying(fn, 'http', 'localhost')
// const r3 = currying(fn, 'http')
// const r4 = currying(fn)
// 提前准备三个参数
// const r1 = currying(fn, 'http', 'localhost', '8080')
// const r2 = r1()
// const r3 = r2()
// const r4 = r3('/a')
// console.log(r4)
// 提前准备两个参数
// const r1 = currying(fn, 'https', 'localhost')
// const r2 = r1()
// const r3 = r2('9999')
// const r4 = r3('/b/c/d')
// console.log(r4)
// 提前准备两个参数
// const r1 = currying(fn, 'https', 'localhost')
// const r2 = r1()
// const r3 = r2('8080', '/a/b/c')
// console.log(r3)
// 提前准备一个参数
// const r1 = currying(fn, 'https')
// console.log(r1('baidu.com', '8080', '/a'))
// console.log(r1('baidu.com')('8080')('/a'))


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/516627.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

周五直播Talk | MIT许逸伦:解锁由物理启发的深度生成模型-从扩散模型到泊松流模型

受到物理、数学等学科的启发&#xff0c;AIGC大模型发展迎来一波技术颠覆。交叉学科里蕴含的巨大能量&#xff0c;为科研人带来创新视角和思维启迪。 北京时间4月28日(周五)10:00&#xff0c;将门-TechBeat社区邀请到麻省理工学院电子与计算机科学系博士生——许逸伦&#xff…

CloudOS:一个平台汇聚云原生技术栈

云原生技术栈是一种新型的技术架构&#xff0c;旨在支持云计算环境下的应用程序开发和部署。它是一种基于容器、微服务、自动化和云平台的技术栈&#xff0c;可以帮助企业更快速、更高效地构建、部署和管理应用程序&#xff0c;成为加速企业数字化业务高效创新、实现企业数字化…

电商如何利用API接口获取商品信息数据

随着电商和互联网的快速发展&#xff0c;API&#xff08;应用程序编程接口&#xff09;的重要性也越来越明显。API接口可以让不同系统之间进行数据交互和通信&#xff0c;进而实现更高效的业务协同和数据共享。在电商业务中&#xff0c;API可以帮助企业实现多个业务系统之间的数…

【数据结构】简单到有摸鱼负罪感的栈的实现

【数据结构】简单到有摸鱼负罪感的栈的实现 一、前言1、什么是栈&#xff1f;2、关于实现的结构选取 二、目标三、实现1、初始化工作2、压栈(push)2.1、图解思路2.2、代码实现 3、弹栈(pop)3.1、图解思路3.2、代码实现 4、打印栈(用于测试)5、返回栈顶数据6、返回栈的数据个数7…

源码环境搭建-唯一客服系统文档中心

运行源码环境 golang语言为跨平台的开发语言&#xff0c;使用唯一客服系统全源码版本&#xff0c;进行二次开发&#xff0c;需要搭建golang运行环境&#xff0c;并且开启go module依赖管理 Windows系统 首先下载golang压缩包&#xff0c;在下面这个地址下载https://studygolang…

第六章:空间解析几何-空间向量

1.空间向量 1.知识概述 1.理解向量的概念和几何表示2.掌握向量的加减法运算3.能够在三维空间内解决常见的向量问题2.向量 1.定义:(在空间内),既有大小又有方向的量叫做(空间)向量。2.表示法:向量可用有向线段表示,有向线段的长度表示向量的大小,有向线段的方向表示向…

网关zuul的使用

前言 Spring Cloud Zuul 主要的功能是提供负载均衡、反向代理、权限认证、动态路由、监控、弹性、安全等的边缘服务。其主要作用是为微服务架构提供了前门保护的作用&#xff0c;同时将权限控制这些较重的非业务逻辑内容迁移到服务路由层面&#xff0c;使得服务集群主体能够具备…

Matlab 迭代法(2)高斯牛顿法

一、思想 高斯牛顿法的对象是最小二乘法。 采用一定的方法对Hession 矩阵进行近似&#xff0c;这样的话可以减少计算量&#xff0c;只需要计算一阶偏导数得到雅可比矩阵即可。 minF(x)|| f(x)||^2 那么x在xk处的增量Δxk出的最小二乘法为 minF(xkΔxk)∣∣f(xk​Δxk​)∣…

10. 100ASK_V853-PRO开发板支持录音和播放音频

0.前言 ​ 本章主要讲述如何使用板载的MIC拾音咪头录音并使用喇叭播放音频。 ​ 音频_开发指南&#xff1a;https://tina.100ask.net/SdkModule/Linux_AudioFrequency_DevelopmentGuide-02/#220-v853 ​ 全志官方音频介绍&#xff1a;https://v853.docs.aw-ol.com/soft/tina…

HBase分布式安装配置

本环节需要使用root用户完成相关配置&#xff0c;安装HBase需要配置前置环境。命令中要求使用绝对路径&#xff0c;具体要求如下: 确认是否完成Hadoop和Zookeeper的分布式&#xff08;Hadoop伪分布式不可以&#xff09;安装部署&#xff0c;没有的话请进行安装部署并启动。完成…

DeSD:用于3D医学图像分割的深度自蒸馏自监督学习

文章目录 DeSD: Self-Supervised Learning with Deep Self-Distillation for 3D Medical Image Segmentation摘要本文方法Deep Self-DistillationDownstream Transfer Learning 实验结果 DeSD: Self-Supervised Learning with Deep Self-Distillation for 3D Medical Image Seg…

数据结构学习记录——集合及运算(集合的表示、并查集、树结构表示集合、集合运算、查找函数、并运算)

目录 集合的表示 集合运算概述 并查集 树结构表示集合 集合运算 查找函数 并运算 集合的表示 集合运算概述 交、并、补、差&#xff0c;判定一个元素是否属于某一个集合 并查集 集合并、查某元素属于什么集合 我们最主要关心的就是集合的两个运算&#xff0c;一个是把…

【网络原理】TCP原理

✨个人主页&#xff1a;bit me&#x1f447; ✨当前专栏&#xff1a;Java EE初阶&#x1f447; 目 录 &#x1f343;一. 确认应答&#x1f342;二. 超时重传&#x1f341;三. 连接管理&#x1f33f;四. 滑动窗口&#x1f33b;五. 流量控制&#x1f340;六. 拥塞控制&#x1f49…

量子计算:当前阶段仍属于热炒概念

一、量子计算为何可能会成为一个风口&#xff1f; 量子计算是利用量子力学原理进行计算的新型计算方式。与传统的经典计算机不同&#xff0c;量子计算机利用量子比特&#xff08;qubits&#xff09;进行信息处理&#xff0c;由于量子比特可以处于叠加态&#xff0c;这使得量子…

【英】考虑多能负荷不确定性的区域综合能源系统鲁棒规划(Python代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

携手共建数字钢铁,Hightopo亮相第三届钢铁展洽会

4 月 26 日备受期待的第三届钢铁展洽会在日照盛大召开。图扑软件作为智慧钢铁行业领先的 2D 和 3D 图形界面可视化解决方案提供商&#xff0c;受邀参与此次展会。 图扑软件携智慧钢铁三维可视化监控体系亮相“钢铁展洽会”&#xff0c;向众多钢铁企业展示了一系列图扑 HT 数字…

性价比最高的护眼台灯是哪款?最好的护眼台灯

不管你处在学生被动学习还是上班后主动学习的阶段&#xff0c;为自己挑选一款合适的台灯非常重要&#xff0c;因为夜晚的氛围能达到很高的学习效率&#xff0c;而台灯可以保证我们有一个舒适的阅读感受。那在为学习需求挑选台灯时&#xff0c;不应该以平价作为选购标准&#xf…

RTC 体验优化的“极值”度量与应用

随着线上互动需求的增加&#xff0c;直播连麦、语音/视频聊天的应用越来越广泛。我们一直在说“追求用户的极致体验”&#xff0c;但是体验是一个抽象的概念&#xff0c;很难量化和统计。如何从用户的行为中得到所在场景的优化“极值”&#xff0c;如何依据“极值”建立统一的质…

macOS Ventura 13.4 RC2(22F63)发布

系统介绍 根据黑果魏叔官网提供&#xff1a;5 月 12 日消息&#xff0c;苹果今天面向开发人员&#xff0c;发布了 macOS Ventura 13.4 的第 2 个候选 RC 版本&#xff08;内部版本号 22F63&#xff09;&#xff0c;距离上个候选版本相隔数天时间。 macOS Ventura 带来了台前调…

VS2022安装NuGet 包

手动安装 在 解决方案资源管理器 中加载项目&#xff0c;然后选择“项目>管理 NuGet 包”。系统会打开“NuGet 包管理器”窗口。 2、 选择“ 浏览 ”选项卡&#xff0c; 使用左上角的搜索框搜索特定包。 从列表中选择一个包&#xff0c;在右侧窗格中显示其信息&#xff0c;…