IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> 前端 JavaScript 之 闭包 _ 讲解版 -> 正文阅读

[JavaScript知识库]前端 JavaScript 之 闭包 _ 讲解版

目录

闭包

?认识函数 :?

函数的两个阶段分别做了什么事情?:?

函数的特点?:?

一个不会被销毁的函数执行空间 :

闭包的作用 :

闭包的缺点 :

间接返回一个函数 :?

闭包 _?语法糖 :?

闭包 _ 简图 :?

闭包 _ 面试题 :?


闭包

是一种函数的高阶应用
官方 :? 函数内的函数?
私人解释 :
需要一个不会被销毁的函数执行空间
函数内直接或间接的返回一个新函数
内部函数 使用 外部函数 的 私有变量
我们就说 内部函数 是 外部函数 的 闭包函数?


?认识函数 :?

+?函数分成两个阶段 :

??1.?函数定义阶段

??2.?函数调用阶段

浏览器的内存使用情况 :

+?浏览器在运行的时候, 会把电脑分配的内容进行再次分配

+?会有两个空间关联比较紧密 :?

??1.?浏览器的 存储空间?-?关联着函数的 定义阶段

??2.?浏览器的 运行空间?-?关联着函数的 调用阶段

函数的两个阶段分别做了什么事情?:?

1.?函数定义阶段?:

??1-1.?在?堆内存?中开辟一段存储空间,?用来保存函数

??1-2.?把?函数体内的代码,?一模一样的复制一份,?以字符串的形式保存在这个函数空间内

????=>?此时任何变量不会进行解析

??1-3.?把函数?堆内存?中的存储空间地址赋值给变量

2.?函数调用阶段 :

??2-1.?按照?变量名(函数名)?内,?存储的地址找到函数?堆内存?中对应的存储空间

??2-1.?在?调用栈(运行空间)?内,?再次开辟一个新的函数执行空间地址,?

把原始函数内的代码复制一份一模一样的到新的执行空间地址

??2-3.?在?新的?执行空间地址内 进行形参赋值

??2-4.?在?新的?执行空间地址内 进行函数内的预解析

??2-5.?在?新的?执行空间地址内?从上到下依次把函数体内的代码当做?js?代码执行一遍

??2-6.?(等到函数所有代码执行完毕)会把?开辟在调用栈?内的?执行空间?销毁

// 函数的两个阶段
// 1. 函数定义阶段
function fn(a, b) {
  // 如果是先形参赋值
  // 那么函数 fn 执行的时候, 先给 a 赋值为 10
  // 然后在预解析的时候, 给 a 变量赋值为一个函数
  // 然后执行代码, 打印的 a 是 函数体

  // 如果是先预解析
  // 那么函数 fn 执行的时候, 先给 a 赋值为一个函数
  // 然后在进行形参赋值的时候, 给 a 赋值为 10
  // 那么执行代码, 打印的 a 是 10
  console.log(a)  // 控制台打印 => ? a() {}
  // 是先形参赋值 => 打印的 a 是 函数体
  console.log(b)  // 控制台打印 => 20
  function a() {}
}

// 2. 函数调用阶段
fn(10, 20)

?


函数的特点?:?

1.?保护私有变量

??=>?因为每一个函数会生成一个独立的私有作用域

??=>?在函数内定义的变量,?我们叫做?私有变量

??=>?该变量只能在该函数作用域及下级作用域内使用,?外部不能使用

2.?函数定义时不解析变量

??=>?函数定义的时候,?函数体内的代码完全不执行

??=>?任何变量不做解析

??=>?直到 执行 的时候才会 解析变量

// 1. 保护私有变量
function fn() {
  // 这个 num 就变成了 私有变量
  var num = 100
  // 只能在这个 fn 区间内 使用
}
fn()
console.log(num)

?

// 2. 函数定义时不解析变量
var num = 100
function fn() {
  // 使用 num 变量的值, 因为自己作用域内没有
  // 所以使用的是全局变量 num
  console.log(num)  // 控制台打印 => 200
  // 所以 => 函数在定义阶段, 不解析变量
}
num = 200
// 函数在定定义阶段的时候, 如果解析了变量
// 那么函数体内保存的代码就是 console.log(100)
// 这里执行的时候, 出现的就是 100, num = 200 这句代码不会生效

// 函数在定义阶段的时候, 如果没有解析变量
// 那么函数体内保存的代码就是 console.log(num)
// 在这里调用的时候, 因为上一行把 num 修改为 200 了, 所以出现的就是 200
fn()

函数的两个阶段 :?

?


一个不会被销毁的函数执行空间 :

+ 当一个函数内返回了一个 复杂数据类型
+ 并且该复杂数据类型, 在函数外部有变量接受的情况
+ 此时函数执行完毕的执行空间, 不会被销毁掉
+ 特点: 延长变量的生命周期

function fn() {
  const obj = { name: 'Jack', age: 18, gender: '男' }
  // 在函数内返回了一个 复杂数据类型 obj
  // 把一个复杂数据类型当做 fn 函数的返回值
  return obj
}
// res 接受的就是 fn 函数内执行后的返回值内容
// 也就是 fn 函数内返回的 复杂数据类型( obj 对象 )
// 当函数体内的 return 执行完毕
// 函数就已经执行完毕了, 此时应该销毁函数的执行空间了
// 为了保证今后 res 随时可以访问到 函数内的 obj 空间
// 所以, 函数的执行空间就不允许被销毁
const res = fn()

// 如何让这个空间销毁
// 让外部接受的变量, 不在指向内部的复杂数据类型
// 只要你再次给 res 进行赋值的时候
// fn 之前的执行空间就销毁了
res = null

闭包的作用 :

1. 可以延长变量的声明周期
? => 因为是一个不会销毁的函数空间
? => 优点: 变量声明周期延长了
? => 缺点: 需要一个 闭包 结构
2. 可以在函数外部访问函数内部的私有变量
? => 需要利用闭包函数
? => 优点: 访问和使用变得更加灵活了
? => 缺点: 需要一个 闭包 结构
3. 可以保护变量私有化
? => 因为只要是函数, 就可以保护私有变量
? => 优点: 变量不会污染全局
? => 缺点: 外部没有办法访问, 如果想要使用, 就得需要写一个 闭包 结构

闭包的缺点 :

+ 内存泄漏
+ 因为闭包的形成必须伴随一个不会销毁的空间
+ 当闭包过多的时候, 就会造成内存过满的情况
+ 如果闭包继续增加, 就会导致内存溢出

// 闭包
function fnA() {
  // 外部函数的 私有变量
  let num = 100
  // 私有函数
  function fnB() {
    // 内部函数使用着外部函数的私有变量
    console.log(num)  // 控制台打印 => 100
    return num
  }
  // 返回的是一个复杂数据类型
  // 并且是一个函数
  return fnB
}
// 外部接受
const res = fnA()
// 需求: 在全局访问 fn 函数的 num 变量
// 因为作用域的原因, 只能在 fn 函数内部才可以访问 num 变量
// 全局不能访问
// console.log(num) // 报错 => num is not defined

// 当我调用 res 这个函数的时候
// 你在函数 fnA 的外面是没有办法拿到 num 这个私有变量的
// res 因为是 fn 函数的内部函数, res 可以访问到 fn 函数的私有变量
// n 接受的就是 fnA 函数内定义的 私有变量 num 的值
const n = res()
console.log(n)  // 控制台打印 => 100


间接返回一个函数 :?

闭包 - 间接返回一个函数 :
+ 在闭包内, 不是直接返回 函数数据类型
+ 而是返回一个 对象 或者 数组 数据类型
+ 在对象内或者数组内, 书写函数数据类型

function fn() {
// 准备私有变量
  let num = 100
  let num2 = 200
// 准备一个对象数据结构
  const obj = {
	getNum: function () { return num },
	getNum2: function () { return num2 },
	setNum: function (val) { num = val }
  }
// 返回这个对象数据结构
  return obj
}
// o 接受的是 fn 函数内的 obj 数据类型
// o 内部有一个叫做 getNum 的函数
const o = fn()
console.log(o)	// {getNum: ?, getNum2: ?, setNum: ?}
// 当我需要访问 fn 内的 num 变量的时候
const n = o.getNum()
console.log(n)	// 100
// 当我需要修改 fn 函数内部的 num 变量的时候
o.setNum(1000)
// 再次访问闭包内的 num
console.log(o.getNum())	// 1000
// =============================================
const o2 = fn()
console.log(o2.getNum())	// 100

function outer() {
  // 准备私有变量
  var n = 100
  var s = 'hello world'
  // 准备一个对象数据结构
  const obj = {
    getN: function () { return n },
    getS: function () { return s },
    setN: function (val) { n = val }
  }
  // 返回这个对象数据结构
  return obj
}
const res = outer()
// 我需要用到 outer 内的 n 变量的时候
console.log(res.getN())
// 我需要用到 outer 内的 s 变量的挥手
console.log(res.getS())
// 我调用 res 内的 setN 函数的时候
res.setN(200)
// 再次访问 outer 内的 n 变量的时候
console.log(res.getN())
// ==========================================
const res2 = outer()
console.log(res2.getN()) // 100

?(? 1 )?


?( 2 )

?( 3 )

( 4 )


闭包 _?语法糖 :?

闭包 - 语法糖 :
+ 在外部函数内返回的对象, 以 getter?( 获取器 ) 和 setter ( 设置器 ) 的形式
+ 返回应该返回的闭包函数
+ 语法:
? {
?? ?get 名字 ( ) { }
?? ?set 名字 ( ) { }
? }
+ 作用:
? => 把 getter 获取的 名字 当做一个对象成员名字
? => 只不过值是一个 ( ... ) 的形式
? => 因为是从后面的函数内返回值得到的数据

function fn() {
  let num = 100
  return {
	// 当你使用 getter 和 setter 的时候, 建议把两个函数名
	// 书写成你要操作的那个变量的名字
	get num () { return num }, // 等价于 num: function () { return num }
	set num (val) { num = val }
  }
}
// res 接受的是一个 fn 函数内的 对象
const res = fn()
console.log(res)

console.log(res.num)	// 100

// 当你给 res.num 进行赋值的时候
// 就是在调用 setter 设置器的函数
// 1000 就是给 val 进行赋值
res.num = 1000
console.log(res.num)	// 1000

?


闭包 _ 简图 :?


闭包 _ 面试题 :?

  function fun(n, o) {
    console.log(o)
    const obj = {
      fun: function(m) {
        return fun(m, n)
      }
    }
    return obj
  }
  var a = fun(0) // undefined

  a.fun(1) // 0
  a.fun(2) // 0
  a.fun(3) // 0

  //   undefined   0     1       2
  var b = fun(0).fun(1).fun(2).fun(3) //

  //   undefined   0
  var c = fun(0).fun(1) //
  
  c.fun(2) // 1
  c.fun(3) // 1

( 1 ) :?

?( 2 ) :?

?( 3 ) :?

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2021-07-29 11:32:24  更:2021-07-29 11:33:07 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/9 3:02:14-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码