粥里有勺糖

vuePress-theme-reco 粥里有勺糖    2018 - 2023
粥里有勺糖 粥里有勺糖

Choose mode

  • dark
  • auto
  • light
关于我
备战春秋
  • 心得总结
  • 校招考点汇总
  • 面经汇总
  • 复习自查
技术笔记
  • 技术教程
  • 模板工程
  • 源码学习
  • 技术概念
  • 个人作品
  • 学习笔记
计算机基础
  • 算法与数据结构
  • 操作系统
  • 计算机网络
  • 设计模式
  • 剑指offer
大前端
  • javascript
  • vue
  • html
  • css
  • 🌏浏览器专题
  • Web性能优化
  • regexp
  • node
面试
  • 问解
  • javascript
  • css
  • 手撕代码
  • 性能优化
  • 综合问题
  • 面经汇总
  • 小程序
手撕代码
  • 数据结构与算法
  • javascript
  • css
个人站点
  • GitHub (opens new window)
  • 博客园 (opens new window)
  • 掘金 (opens new window)
线上作品
  • 轻取(文件收集) (opens new window)
  • 个人图床 (opens new window)
  • 考勤小程序 (opens new window)
  • 时光恋人 (opens new window)
  • 在线简历生成 (opens new window)
留言板
Github (opens new window)
author-avatar

粥里有勺糖

285

文章

40

标签

关于我
备战春秋
  • 心得总结
  • 校招考点汇总
  • 面经汇总
  • 复习自查
技术笔记
  • 技术教程
  • 模板工程
  • 源码学习
  • 技术概念
  • 个人作品
  • 学习笔记
计算机基础
  • 算法与数据结构
  • 操作系统
  • 计算机网络
  • 设计模式
  • 剑指offer
大前端
  • javascript
  • vue
  • html
  • css
  • 🌏浏览器专题
  • Web性能优化
  • regexp
  • node
面试
  • 问解
  • javascript
  • css
  • 手撕代码
  • 性能优化
  • 综合问题
  • 面经汇总
  • 小程序
手撕代码
  • 数据结构与算法
  • javascript
  • css
个人站点
  • GitHub (opens new window)
  • 博客园 (opens new window)
  • 掘金 (opens new window)
线上作品
  • 轻取(文件收集) (opens new window)
  • 个人图床 (opens new window)
  • 考勤小程序 (opens new window)
  • 时光恋人 (opens new window)
  • 在线简历生成 (opens new window)
留言板
Github (opens new window)
  • Javascript

    • 简介
    • 获取某年某月的天数
    • 日期格式化
    • 展开多级数组
    • 判断数据类型的方案
    • 文件上传与下载
    • 柯里化
    • 闭包
    • delete
    • 垃圾回收
    • 节流与防抖
    • apply,call,bind
    • blob与file
    • Event Loop
    • Promise
    • 定时器
    • 原型与原型链
    • 作用域
    • 执行上下文栈
    • 变量对象
    • 作用域链
    • ECMAScript规范解读this
    • 执行上下文
    • 参数按值传递
    • 类数组与arguments
    • 浮点数
    • Symbol的用法
    • 箭头函数
    • 类型转换
    • 获取dom元素的方式
    • 浅拷贝与深拷贝
    • ES6+的新语法糖和方法整理
    • 学习过程中学到的一些取巧之法

闭包

vuePress-theme-reco 粥里有勺糖    2018 - 2023

闭包

粥里有勺糖 2020-04-14 大前端javascript

# 闭包

# 定义

  • 简单定义:函数 A 内部有一个函数 B,函数 B 可以访问到函数 A 中的参数与变量,那么函数 B 就是闭包
  • MDN定义(理论上)

闭包是指那些能够访问自由变量(在函数中使用的,但既不是函数参数也不是函数的局部变量的变量)的函数。

  • 实践

使创建它的上下文已经销毁,它仍然存在,在代码中引用了自由变量 垃圾回收机制没有把当前变量和参数回收掉,这样的操作带来了内存泄漏的影响

通常就是通过闭包间接访问函数内部的变量,也就是说闭包就是能够读取其它函数内部变量的函数

function A() {
    let a = 1
    windos.B = function() {
        console.log(a)
    }
}
A()
B() // 1
1
2
3
4
5
6
7
8
function a() {
    let i = 0;
    return function b(){
        console.log(++i)
    }
}
let fn = a()
fn() // 1
fn() // 2
fn() // 3
1
2
3
4
5
6
7
8
9
10

# 使用时期

需要重用一个变量,又要保护变量不会被污染

# 特点

  • 参数与变量不会被垃圾回收机制回收

# 与作用域相比较

  • 全局变量
    • 优:可重用
    • 缺:容易污染
  • 局部变量
    • 优:不会被污染,仅函数内部可用
    • 缺:不可重用

# 缺点

  • 比普通函数占用更多的内存。
    • 释放:将引用内层函数对象的变量赋值为null
    • 内存泄漏的影响,当内存泄漏到一定程度会影响你的项目运行变得卡顿等等问题

内存泄露

指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果

# 用途

  • 将一个变量长期驻扎在内存当中可用于循环取值
  • 私有变量计数器,外部无法访问,避免全局变量额污染
  • 重用一个变量,又保护变量不被污染的一种机制。

# 经典例题

// 本意输出1 2 3 4 5
for (var i = 1; i <= 5; i++) {
  setTimeout(function timer() {
    console.log(i)
  }, i * 1000)
}
// 错误输出 6 6 6 6 6
1
2
3
4
5
6
7
    1. 闭包解决----IIFE(立即执行函数)
for (var i = 1; i <= 5; i++) {
    (function (j) {
        setTimeout(function timer() {
            console.log(j)
        }, j * 1000)
    })(i)
}
1
2
3
4
5
6
7
    1. 闭包解决----函数嵌套
for (var i = 1; i <= 5; i++) {
    function b(j){
        setTimeout(function timer() {
            console.log(j)
        }, j * 1000)
    }
    b(i)
}
1
2
3
4
5
6
7
8
    1. setTimeout的第三个参数
for (var i = 1; i <= 5; i++) {
    setTimeout(function timer(j) {
        console.log(j)
    }, i * 1000, i)
}
1
2
3
4
5
    1. 使用let定义i
for (let i = 1; i <= 5; i++) {
    setTimeout(function timer() {
        console.log(i)
    }, i * 1000)
}
1
2
3
4
5

# 结合作用域链

var data = [];
for (var i = 0; i < 3; i++) {
  data[i] = function () {
    console.log(i);
  };
}
data[0](); // 3
data[1](); // 3
data[2](); // 3
1
2
3
4
5
6
7
8
9
  1. 执行到 data[0] 时
globalContext = {
    VO:{
        data:[...],
        i:3
    }
}
1
2
3
4
5
6
  1. 执行data[0]
data[0]Context = {
    AO:{
        arguments:{
            length:0
        }
    },
    Scope:[AO,globalContext.VO]
}
1
2
3
4
5
6
7
8
  1. 因为data[0]的AO中没有i,所以从 globalContext.VO 中去查找
  2. data[1],data[2] 同理

使用匿名函数包裹形成闭包

var data = [];
for (var i = 0; i < 3; i++) {
  data[i] = (function (i) {
        return function(){
            console.log(i);
        }
  })(i);
}
data[0](); // 0
data[1](); // 1
data[2](); // 2
1
2
3
4
5
6
7
8
9
10
11
  1. 在执行到 data[0]时
globalContext = {
    VO:{
        data:[...],
        i:3
    }
}
1
2
3
4
5
6
  1. 执行data[0]时
data[0]Context = {
    AO:{
        arguments:{
            length:0
        }
    },
    Scope:[AO,匿名函数Context.AO,globalContext.VO]
}
匿名函数Context= {
    AO:{
        arguments:{
            0:0,
            length:1
        },
        i:0
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  1. data[0]沿着作用域链找到了匿名函数Context.AO上的i,此时就不会再继续查找了
  2. data[1],data[2] 同理

参考

JavaScript深入之闭包 (opens new window)

Edit this page (opens new window)
Last Updated: 2022/5/15 12:46:34