夜下客

繁体版 简体版
夜下客 > JS修炼法则 > 第7章 闭包

第7章 闭包

闭包

引子: 面试经常会问,谈谈你对闭包的理解。

网红答案很无聊的:闭包就是能够读取其他函数内部变量的函数,闭包是指那些能访问自由变量的函数,闭包能把变量保存在内部中,blabla。大家都从网上百度一下,浪费时间去背诵一些表象,是一件很无趣的事情。

在JS中,闭包对应的概念就是函数。不要把作用域或执行上下文当作闭包。

执行上下文我会在文末进行讲述。

每次我基本都会给面试官一个不同的角度去看,从源头去看这个东西:从 JS 函数创建执行开始讲,一个函数执行过程前后的内存回收过程,这种过程会存在什么问题,该问题就是闭包产生的原因了,闭包其实就是函数执行的一种保护机制,闭包又会带来哪些问题,如何去解决这些问题,闭包的理论作用(用途),再谈谈自己在项目中用闭包实现了什么需求,再可以谈一下 redux 中改造 dispatch 使用了闭包。

简单总结:JS 函数创建执行,存在的问题,解决方案是闭包,闭包坏处,如何解决,闭包作用,何时使用闭包,项目中闭包的实践,闭包的应用。

个性总结:闭包是 JS 函数执行的一种保护机制。

复述一下上面的相关过程,作为闭包的总结。

1. 函数创建、执行

函数创建时,在堆内存中存储字符串。函数执行时,会创建一块栈内存空间,初始化其中的变量,执行函数体,当函数执行完毕时,这块内存空间会被销毁,其中的变量也随之销毁。下次调用该函数时会重新开辟一块新内存空间,重复上述操作。

详细见文章 0005 JS 函数

2. 存在的问题

但这种机制会产生问题。我们假设当前函数内返回一个函数,或者接收一个函数作为参数,且都引用了外层函数的变量,外层函数执行完毕,其所占用的栈内存空间被销毁,里面的变量也随之销毁。但内层函数消费了外层变量,同时内层函数被抛出到外部,如果此时执行内层函数,因引用变量已经被销毁,无法获取引用变量的值。

此时,这种函数执行机制就产生了问题。这也就是闭包诞生的原因。

3. 解决方案:闭包

这时,会把函数和当前执行上下文一起保存到堆内存中,我们把这个整体成为闭包。

根据标准定义,可以把闭包归纳成两个组成部分:

(1)环境:

环境:函数的词法环境(执行上下文的一部分)

标识符列表:函数中用到的未声明的变量

(2)函数体

4. 闭包坏处

(1)内存消耗大,内存泄漏 (2)改变父函数内部变量的值

5. 如何解决

在退出函数之前,将不使用的全局变量全部删除,即手动解除引用 赋值为 null。

6. 闭包作用

(1)模拟块级作用域,使用立即执行函数 IIFE 即可。 (2)模块化,可以实现对私有变量的封装 (3)可以在函数外部读取到函数内部的变量 (4)将内部变量始终保存在内存中

7. 何时使用闭包

希望重用一个对象,又保护对象不被污染篡改时。

防止变量污染的新方案: ES6 let const

8. 项目中闭包的实践

9. 闭包的应用

分别从 React、Vue、设计模式三个纬度进行分享。

9-1. Redux dispatch

function applyMiddleware(...middlewares) {

// 返回 增强版的createStore方法(增强体现在middlewares对dispatch的改造)

return (createStore) => (...args) => {

// ...arg就是reducer,[preloadedState]

const store = createStore(...args)

// 初始化dispatch,不允许构造中间件过程中 调用dispatch

// 后续会被更改,映射到闭包中

『加入书签,方便阅读』