前端基石:闭包

简介: 本文主要讲闭包

网络异常,图片无法展示
|


一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第5天,点击查看活动详情


什么是闭包?


  • ✅ 官方说法:闭包就是指有权访问另一个函数作用域中的变量的函数。
  • ✅ MDN说法:闭包是一种特殊的对象。它由两部分构成:函数,以及创建该函数的环境。环境由闭包创建时在作用域中的任何局部变量组成。
  • ✅ 自我理解:把函数执行,形成私有上下文,并且保存和保护私有变量的机制,称之为“闭包” ,它是一种机制。


函数的执行看闭包


过程


  1. 形成私有上下文
  2. 进栈执行
  3. 一些列操作
  1. 初始化作用域链(两头<当前作用域,上级作用域>)
  2. 初始化this
  3. 初始化arguments
  4. 赋值形参
  5. 变量提升
  6. 代码执行
  1. 遇到变量就先看是否是自己私有的,不是自己私有的按照作用域链上查找,如果不是上级的就继续线上查找,,直到 EC(G),变量的查找其实就是一个作用域链的拼接过程,拼接查询的链式就是作用域链。
  1. 正常情况下,代码执行完成之后,私有上下文出栈被回收。但是遇到特殊情况,如果当前私有上下文执行完成之后中的某个东西被执行上下文以外的东西占用,则当前私有上下文就不会出栈释放,也就是形成了不被销毁的上下文,闭包。


三种情况


  • 当前上下文的某些东西被上下文以外的某些东西占用,那么当前上下文就不会被释放。
  • 如果没有被占用就是执行完成之后就被释放。
  • 除了这上面两种情况还有一种情况是,上下文没有被占用,但是要紧接着被用一次,这样没有用完之前是不能释放的,用完在释放,这样就形成了一个临时不被释放 )

函数每一次执行 都是从新形成一个全新的私有上下文,和之前执行产生的上下文没有必然的联系


闭包的作用


函数执行会形成全新的私有上下文,这个上下文可能被释放,也可能不被释放,不论是否被释放,它的作用是:


  1. 保护:划分一个独立的代码执行区域,在这个区域中有自己私有变量存储的空间,而用到的私有变量和其它区域中的变量不会有任何的冲突(防止全局变量污染)
  2. 保存:如果上下文不被销毁,那么存储的私有变量的值也不会被销毁,可以被其下级上下文中调取使用

市面上一般认为只有形成的私有上下文不被释放,才算是闭包(因为如果一但释放,之前的东西也就不存在了);还有人认为,只有一下级上下文用到了此上下文中的动西才算闭包;


练习题


let x = 5;
const fn = function fn(x) {
  return function(y) {
    console.log(y + (++x));
  }
}
let f = fn(6);
f(7); // 14
fn(8)(9); // 18
f(10); // 18
console.log(x); // 5
  1. 变量提升(跳过,没有var、function...)
  2. 代码执行
  1. x -> 5
  2. fn -> 0x 001
  3. fn (6) 函数执行
  1. 创建私有上下文,创建活动对象 AO(fn1)
  2. 初始化作用域链,<<EC(fn1), EC(G)>>
  3. 初始化 this
  4. 初始化 arguments
  5. 形参赋值
  6. 变量提升
  7. 函数执行,返回一个小函数,为小函数创建新的堆内存存储函数内容(作用域,代码字符串,键值对)
  1. f = 返回的小函数,也就是新的函数执行,f -> 0x 002,由于0x 002 被 f 占用,所用0x 002 不能被释放,所以它的上下文(作用域)也不能被释放。
  2. f(7) 函数执行
  1. 创建私有上下文,创建活动对象 AO(f1)
  2. 初始化作用域链,<<EC(f1), EC(fn1)>>
  3. 初始化 this
  4. 初始化 arguments
  5. 形参赋值
  6. 变量提升
  7. 函数执行,y = 7,x 当前作用域没有,沿着作用域链往上找,所以在 EC(fn1) 中查找 x = 6, y +(++ x) = 14
  1. fn(8) 函数执行
  1. 创建私有上下文,创建活动对象 AO(fn2)
  2. 初始化作用域链,<<EC(fn2), EC(G)>>
  3. 初始化 this
  4. 初始化 arguments
  5. 形参赋值
  6. 变量提升
  7. 函数执行,返回一个小函数,为小函数创建新的堆内存存储函数内容(作用域,代码字符串,键值对)
  1. fn(8)(9) 函数执行
  1. 创建私有上下文,创建活动对象 AO(f2)
  2. 初始化作用域链,<<EC(f2), EC(fn2)>>
  3. 初始化 this
  4. 初始化 arguments
  5. 形参赋值
  6. 变量提升
  7. 函数执行,y = 9,x 当前作用域没有,沿着作用域链往上找,所以在 EC(fn2) 中查找 x = 8, y +(++ x) = 18
  1. f(10) 函数执行
  1. 创建私有上下文,创建活动对象 AO(f3)
  2. 初始化作用域链,<<EC(f3), EC(fn1)>>
  3. 初始化 this
  4. 初始化 arguments
  5. 形参赋值
  6. 变量提升
  7. 函数执行,y = 10,x 当前作用域没有,沿着作用域链往上找,所以在 EC(fn1) 中查找 x = 7, y +(++ x) = 18

网络异常,图片无法展示
|


下面子看一个例子,看大家能不能自己跟着上一个例子的思路来解出答案了。

let a = 0,
    b = 0;
let A = function(a) {
  A = function (b) {
    console.log(a + b++);
  }
  console.log(a); 
}
A(1); // 1
A(2); // 3

网络异常,图片无法展示
|


面试中如何回答闭包问题


其实在面试中存在很多的闭包问题,笔试、问答都有可能,并且面试可能不会直接问闭包,问的是其他问题,但是可能会用到闭包。


在面试中如果是如上练习题的笔试或者看代码说结果的问题,那可参照上面的思路,这类题目不论多么复杂,万变不离其宗。


如果在面试中是问答,不要只会回答闭包的概念。还要学会从理论、底层运行机制、实践等角度来回答闭包,不仅能看出你基础能力的深度,也能看出你基础能力的广度。 在我之前的文章 “四说闭包” 惊艳面试官|8月更文挑战中有较为深入的理解

  1. 先说闭包是什么?
  2. 在说函数的创建和执行看闭包(引述:堆栈、EC、AO、VO、scope)
  3. 然后说闭包的作用以及在项目中的引用场景,以及带来的问题
  4. 最后说闭包引发的高级编程技巧,在框架源码中的使用,或者自己写类库的怎么使用


总结


闭包不管是日常的研发过程中还是面试中都是一个常见的场景,深入理解必将让你在日常开发和面试中如鱼得水。


目录
相关文章
|
6月前
|
前端开发 JavaScript 开发者
Web前端开发中的JavaScript闭包应用
JavaScript闭包是Web前端开发中常见的概念,它可以帮助开发者解决作用域问题,提高代码的可读性和可维护性。本文将介绍JavaScript闭包的基本概念和应用,以及如何在Web前端开发中使用闭包。
67 3
|
6月前
|
移动开发 前端开发 API
深入理解前端路由:构建现代 Web 应用的基石(上)
深入理解前端路由:构建现代 Web 应用的基石(上)
深入理解前端路由:构建现代 Web 应用的基石(上)
|
6月前
|
自然语言处理 前端开发 JavaScript
No103.精选前端面试题,享受每天的挑战和学习(闭包)
No103.精选前端面试题,享受每天的挑战和学习(闭包)
|
20天前
|
存储 监控 前端开发
掌握微前端架构:构建未来前端应用的基石
【10月更文挑战第12天】随着前端技术的发展,传统的单体应用架构已无法满足现代应用的需求。微前端架构通过将大型应用拆分为独立的小模块,提供了更高的灵活性、可维护性和快速迭代能力。本文介绍了微前端架构的概念、核心优势及实施步骤,并探讨了其在复杂应用中的应用及实战技巧。
|
25天前
|
存储 前端开发 JavaScript
前端必备知识:闭包的概念、作用与应用
前端必备知识:闭包的概念、作用与应用
16 1
|
20天前
|
JSON 前端开发 JavaScript
构建现代前端应用的基石
【10月更文挑战第13天】构建现代前端应用的基石
|
6月前
|
移动开发 前端开发 数据可视化
前端HTML:构建网页的基石
前端HTML:构建网页的基石
40 0
|
3月前
|
缓存 前端开发 JavaScript
彻底理解前端闭包
【8月更文挑战第7天】彻底理解前端闭包
30 1
|
3月前
|
开发者 图形学 C#
深度解密:Unity游戏开发中的动画艺术——Mecanim状态机如何让游戏角色栩栩如生:从基础设置到高级状态切换的全面指南,助你打造流畅自然的游戏动画体验
【8月更文挑战第31天】Unity动画系统是游戏开发的关键部分,尤其适用于复杂角色动画。本文通过具体案例讲解Mecanim动画状态机的使用方法及原理。我们创建一个游戏角色并设计行走、奔跑和攻击动画,详细介绍动画状态机设置及脚本控制。首先导入动画资源并添加Animator组件,然后创建Animator Controller并设置状态间的转换条件。通过编写C#脚本(如PlayerMovement)控制动画状态切换,实现基于玩家输入的动画过渡。此方法不仅适用于游戏角色,还可用于任何需动态动画响应的对象,增强游戏的真实感与互动性。
88 0
|
3月前
|
自然语言处理 前端开发 JavaScript
前端进阶必读:JS闭包深度解析,掌握这一特性,你的代码将焕然一新!
【8月更文挑战第23天】闭包是JavaScript的一项高级功能,让函数能够访问和操作外部函数作用域中的变量。本文深入解析闭包概念、组成及应用场景。闭包由函数及其词法环境构成,通过在一个函数内定义另一个函数来创建。它有助于封装私有变量、维持状态和动态生成函数。然而,不当使用闭包可能导致内存泄漏或性能问题。掌握闭包对于实现模块化代码和成为优秀前端开发者至关重要。
40 0