橙晨燕

vuePress-theme-reco Visionwuwu    2020 - 2021
橙晨燕 橙晨燕

Choose mode

  • dark
  • auto
  • light
主页
分类
  • 大学课程
  • 前端
  • 力扣算法
  • 随笔
标签
时间线
收藏集
  • 爱看的书
  • 爱追的剧
其他
  • 每日壹题 📚
  • CET-4 🛰️
  • 技术文档 📖
  • 收藏文章 📮
  • 项目展示 🚀
  • 项目总结 📝
  • 工具箱 📦

    • 图片压缩
    • 阿里云
    • 腾讯云
    • 掘金
    • CSDN
关于我
GitHub
author-avatar

Visionwuwu

25

文章

8

标签

主页
分类
  • 大学课程
  • 前端
  • 力扣算法
  • 随笔
标签
时间线
收藏集
  • 爱看的书
  • 爱追的剧
其他
  • 每日壹题 📚
  • CET-4 🛰️
  • 技术文档 📖
  • 收藏文章 📮
  • 项目展示 🚀
  • 项目总结 📝
  • 工具箱 📦

    • 图片压缩
    • 阿里云
    • 腾讯云
    • 掘金
    • CSDN
关于我
GitHub
  • 面试宝典

    • 常见的css面试
    • 堆栈内存
    • 闭包
      • 闭包如何产生的
      • 闭包的作用
      • 闭包的缺点
      • 面试题目练习
      • 总结
    • 深克隆和浅克隆
    • 面向对象
    • 异步和同步
    • 防抖和节流
    • 前端性能优化之CRP
    • 常见的vue面试题
    • 数组去重
    • 前端经典算法
    • 手写Promise

闭包

vuePress-theme-reco Visionwuwu    2020 - 2021

闭包


Visionwuwu 2020-05-25 javascript

  • 闭包如何产生的
  • 闭包的作用
  • 闭包的缺点
  • 面试题目练习
  • 总结

# 闭包如何产生的

一个内部函数被外部变量引用了就形成闭包,闭包会导致原有的作用域链,不被销毁,造成内存泄露。 从原理上来讲就是作用域链得不到释放。可能你需要了解作用域,作用域链。

# 闭包的作用

  1. 实现公有变量
  2. 缓存变量
  3. 可以实现封装,属性私有化
  4. 模块化开发,防止污染全局变量

# 闭包的缺点

  1. 造成内存泄露,也就是占用内存。

# 面试题目练习

  • 小练习一
let a = 1;
function fn(){
    let a = 2;
    return ()=>{
        return a++;
    }
}
let c = fn()
let d = fn()
console.log(c())
console.log(d())
console.log(c())
1
2
3
4
5
6
7
8
9
10
11
12
解析

答案

  • 输出2
  • 输出2
  • 输出3
GO = {
    ...window,
    a: 1,
    c: undefined,
    fn: function fn(){...} // 定义
    window: this
}
// VO指向GO
VO(G) = {
     ...window,
    a: 1,
    c: undefined,
    fn: function fn(){...} // 定义
    fn[[scope]]: this, // VO(G)
    window: this
}
// 创建执行环境栈
ECStack = []
// 创建全局执行环境,压缩到栈顶,获得优先执行权
ECStack = [
    EC(G) = {
        VO(G) = {
            ...window,
            a: 1,
            fn: function fn(){...} // 定义
            fn[[scope]]: this, // VO(G)
            window: this
        }
    }
]
// 函数fn执行创建EC(fn),创建作用域链,并初始化为当前执行函数的scope属性
//匿名函数定义初始化scope为当前环境
ECStack = [
    EC(fn) = {
        [scope]: VO(G), // 当前作用域指向全局变量对象VO(G)
        AO: {
            a: 2,
            arguments: [],
            name: 'fn',
            length: 0,
            "匿名函数": ()=>{return a++;} // 匿名函数定义
            "匿名函数[[scope]]": this, // 匿名函数scope属性初始化当前环境,VO(fn)
        },
        scopeChain: <AO(fn), AO(fn)[scope]>, // 初始化当前作用域为函数fn的scope属性,并将函数fn的AO对象指向链表最顶端
    },
    EC(G) = {
        VO(G) = {
            ...window,
            a: 1,
            fn: function fn(){...} // 定义
            fn[[scope]]: this, // VO(G)
            window: this
        }
    }
]
// 函数fn执行完毕后销毁了,所以fn的执行上下文EC(fn)没有用了从堆栈内存中删除->出栈,接着fn返回来的函数的引用被保存到c上c执行创建执行上下文EC(c),创建作用域链,并初始化为函数c(匿名函数)的scope所包含的对象,VO(fn)
ECStack = [
    EC(c) = {
        [scope]: VO(fn), // 当前作用域指向全局变量对象VO(G)
        AO: {
            arguments: [],
            name: 'fn',
            length: 0,
        },
        scopeChain: <AO(c), AO(c)[scope]>, // 初始化当前作用域为函数c的scope属性,并将函数c的AO对象指向链表最顶端
    },
    EC(fn) = {}, // 销毁了
    EC(G) = {
        VO(G) = {
            ...window,
            a: 1,
            fn: function fn(){...} // 定义
            fn[[scope]]: this, // VO(G)
            window: this
        }
    }
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
  • 小练习二
// 以下代码输出结果
let a = 1;
function A(a){
    A = (b)=>{
        console.log(a + b++)
    }
    alert(a)
}
A(2)
A(5)
1
2
3
4
5
6
7
8
9
10
解析

答案

  • 先alert '2'
  • 输出 7

# 总结

闭包随处可见,但是我们在平常的工作中我们尽量避免闭包的产生,但如果能合理利用闭包的特点还是非常有用的。

在 GitHub 上编辑此页 !
最后一次更新: 5 个月前