js小黄书笔记一:闭包和作用域

⭐️ 本书所有知识点均可在《你不知道的JavaScript()》中找到出入点,如果想要进阶原生js的同学,推荐你去把《你不知道的JavaScript》上中两册精读一遍,我相信你能收获很多东西。

🔑:以下的内容是我结合上中两册总结出来的笔记,文笔有限,随缘更新 。

一,闭包和作用域

0. 作用域

什么是作用域:用来存储变量,存储变量的值,变量的增删改查对接的地方【也就是变量能访问到的范围较作用域】

栈内存和堆内存

  • 函数执行形成栈内存,所有的变量都是在栈内存中存储的,也就是所谓的作用域【基本数据类型存储在栈内存】【复杂数据类型存储在堆内存】

  • 作用域中的变量的查找方式是向上级作用域查找,不能向同级作用域查找

    PS:

1. 编译原理

在浏览器中共有三个角色负责程序的执行,分别是:编译器执行引擎作用域

我们来看下 var a = 20 这段代码的编译过程

  1. 首先,遇到 var a ,编译器会询问作用域是否有同名 a 这个变量存在,若存在,则跳过这一步;若不存在,编译器会要求当前作用域生成一个 变量a 【在Es6中,使用let,const关键字进行重复声明会报错,而var具有预解析,不会报错】
  2. 接下来编译器会为执行引擎生成代码,这些代码会用来处理 a = 20 这一步。
  3. 引擎运行,首先会询问当前作用域有没有一个叫 a 的变量,若有,则把这个变量的值赋值为20;

2. LHS和RHS查询

执行引擎在进行变量操作的时候,会对不同的情况对作用域中的变量进行 LHS和RHS查询

LHS:查找变量的存储地址【赋值的时候,把值存储在这个变量地址中】

RHS:查找变量的值【获取一个变量的值的时候】

观察下面的代码

a = 20;					// LHS查询,获取a变量,把a变量的值修改成20【基于作用域】
console.log(a)	// RHS查询,获取a的值并打印【基于作用域】

LHS和RHS的区别

两者都是基于:当前作用域 -> 上级作用域 -> 上级作用域…

  • LHS:若在所有作用域作用域链上都没找到变量,则在顶层作用域(global)中创建变量并赋值等等
  • RHS:若在所有作用域作用域链上都没找到变量,则抛出错误
let aaa = 0						// 全局作用域创建aaa,赋值成0
;(function() {	
  let aaa = 1					// 创建aaa变量,赋值成1
  ;(function() {
    console.log(aaa)	// RHS查询,在上级作用域中找到aaa, 值为1
    aaa = 1000				// LHS查询,在上级作用域中找到aaa, 修改值为1000
    console.log(aaa)	// RHS查询,在上级作用域中找到aaa, 值为1000
  })()
  console.log(aaa)		// RHS查询,在当前作用域中找到aaa, 值为1000
})()
console.log(aaa)			// RHS查询,在当前作用域中找到aaa, 值为0

3. 词法作用域

词法作用域完全由编写代码期间函数的位置来顶底决定

词法作用域:函数在被定义后,不管这个函数之后在哪个别的作用域里被调用,他的作用域都是从被定义的位置开始查找的【比如:函数在哪里定义,他的作用域就在哪,无论实在哪里被调用】

举个例子:

这里形成了三个作用域:

  1. 全局window作用域
  2. fn1函数作用域
  3. fn2函数作用域

虽然 fn1 函数是在 fn2 中被调用的,但是他的作用域在代码被编写的的时候就天注定了:fn1的上级作用域是window,而不是fn2,所以在寻找 value 变量的时候会向 window 作用域找【词法作用域决定函数的作用域】

所以叫做词法作用域,因为他的作用域是编写时就天注定了,不管是在哪里被调用

4. 函数作用域

函数作用域定义:

⭐️ ​类似于一个封闭的小气泡,外部不能访问这个气泡里边的变量和函数,只能里面函数或者变量访问外面的变量;【变量访问遵循作用域链原则,遵循词法作用域原则】

函数作用域的优点:

  1. 隐藏函数内部实现,只暴露一个接口。类似黑盒,把处理数据的代码封装到一个黑盒中,只暴露入口(参数)和出口(return)

  2. 避免变量冲突。一个函数作用域中可各种变量和函数,并且把这些变量和全局作用域隔绝,不会污染全局作用域

  3. 闭包。用过的都说好,函数执行后对外 return 一个对象或者函数,用来突破函数作用域不能被外部作用域访问的限制

  4. 保护函数作用域中的变量(外部无法访问内部)

函数作用域的缺点:

需要定义一个函数(污染一个变量),再执行该函数(可以使用IIFE立即执行函数优化)

5. 块级作用域

首先我们来看下Es6之前的 var 关键字,这个关键字是没有块级作用域的

var

⭐️ var没有块级作用域的(try…catch的catch中有),使用一个for循环都会污染到全局作用域

let,const

接着就是 let,const 这两个关键字,在遇到 {} 会产生块级作用域,类似函数作用域

⭐️ 块级作用域最大的作用就是,它会加入到作用域查找链中【超重要!】,并且块级作用域和闭包进行配合可以替代函数作用域

块级作用域的优点:

  1. 可以替代部分IIFE的工作(具体看需求)
  2. 变量污染程度大量减少,比函数作用域更加容易创建(for循环有块级作用域)
  3. 优化垃圾回收机制(若产生闭包,函数私有作用域不会删除,但是如果内部有大量的数据残留,可以使用块级作用域 P33)
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PNLxMrSl-1619412389103)(%E4%BD%A0%E4%B8%8D%E7%9F%A5%E9%81%93%E7%9A%84JS%E6%80%BB%E7%BB%93.assets/image-20210321211750152.png)]

6. 提升

其实,在JS代码执行前,编译器会对我们写好的代码进行一个预编译,这个就叫提升。

⭐️ ​这个过程就是把代码中的var a var b function a() {...} 这样的变量申明提升到作用域的最前面【提升只针对 var 和 function关键字】

  • 首先看 var 的变量提升【只提升申明,不赋值】

    其中的原因就是,编译器首先会把所有的 var 申明提升到作用域最前面,再执行代码

    真实运行代码的状况其实是这样的:

  • 再来看 function 关键字的提升【又提升申明,又赋值】

    用function声明的函数,也会像var一样提升到最前面,但是这个关键字是 提升+申明

    代码真实运行情况:

  • 两者提升的优先级

    function 的提升优先级 > var关键字优先级

    上面代码的真实运行情况

  • 匿名函数没有提升

    当我们使用function关键字作为匿名函数时,没有变量提升

    但是!有名函数就会有提升

7.{}中的提升

😋 在ES6之前是没有块级作用域这个说法的,所以在 {} 中使用 varfunction 关键字定义的变量和函数,本质上是属于全局作用域的【函数中的变量和函数除外,因为函数被编写时就有词法作用域】,所以会被提升,具体的提升规则:

  1. var 变关键字的提升规则不变
  2. function 关键字不再提升函数整体,只提升函数名,和 var 一样的形式

⭐️ 请记住一点:在块级作用域中的匿名有名函数没有提升

8. 闭包

闭包可以说是 javaScript中最重要的技术之一,在 javaScript 中,闭包遍地可见:定时器,DOM事件监听器,Ajax请求等等以及任何传递回调函数的地方,都是用了闭包

⭐️ 专业点说就是:闭包就是 基于词法作用域+函数 来突破 作用域限制 的一种工具

⭐️ 用我自己的话说:函数执行后形成不销毁的作用域,且该作用域中的变量能通过一个函数被任何其作用域访问,这就是闭包【不销毁的关键就是该作用域中的对象或者函数被别的作用域指向,垃圾回收机制不会回收】

💥 PS:块级作用域和闭包结合也更香哦~​

  1. 来看一个闭包突破作用域限制的例子:

    🅰️ 我们知道,函数具有词法作用域,即被编写时就具有天注定的作用域,无论这个函数是在哪里调用,执行的时候,函数内部的变量的查找规则始终是从被编写的地方开始查找

    🅱️ 而在JavaScript中,函数是一等公民,可以作为函数参数或者函数返回值四处传递。

    ⭐️ 结合上面两点,我们就可以把一个在 fn 作用域中的函数作为参数传递到别的作用域中【函数是一等公民】,在别的作用域中执行的时候,基于词法作用域的特性,该函数【返回的匿名函数的词法作用域】是能够访问到fn 内的作用域的!!!

    综上所述:我们实现突破了函数作用域的限制,使得函数外能访问函数内的这种方式

  2. 闭包产生的原理

    1. 函数执行会创建自己的私有作用域,该作用域的生命周期是函数执行结束
    2. 若函数作用域中有一个或者多个变量被外部作用域指向时,作用域就不会回收销毁
    3. 利用上面的两个特性就能够形成不销毁的函数作用域,这也是闭包能产生的原因

    附上丑图一张:

  3. 闭包的运用:

    • 各种回调函数

    • 自执行函数

    • 柯理化函数

    • 模块模式

    • …还有一大堆…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值