如何彻底理解变量提升,作用域链,闭包?(聊聊js的上下文)

还是先说说文章的由来。

今年其实立了挺多flag的,有源码阅读,有ui框架,有脚手架,有前端监控,还有react源码。完成的怎么样,暂且不提(年底一块总结),至少可以把前端的知识点梳理一遍。

所以这篇文章,来盘一盘js的上下文,彻底理解js一些奇怪的特性。

什么是上下文?

在执行一段js代码之前,会先进行编译,编译的过程中会生成上下文。

首先会生成一个全局上下文。

上下文中包含两块内容,分别是变量环境跟词法环境。

如果代码中有函数,则会生成函数上下文。

保存上下文的是一个栈结构,也就是后进先出。

为什么会变量提升?

在编译阶段,会将变量的声明存在变量环境中。

css 复制代码
    console.log(a)
    console.log(b)
    var a = 1
    function b() {
       var c = 2
    }
    b()

比如这样一段代码,编译阶段上下文情况如下。

然后开始执行代码。

1.输出a为undefined,b为function。

2.将a赋值为1

3.执行函数b,生成b函数的上下文。

此时的上下文情况。

4.将c赋值为2

等函数执行完毕,则会销毁函数上下文。

但是,如果函数中又调用了其他函数,那么就会创建新的函数上下文,压入栈中,也叫调用栈。

栈的大小是有限制的,如果说函数嵌套(一般是递归),有可能造成栈溢出

变量提升,有什么问题?

变量提升其实不太符合常规逻辑。

而且会带来一些问题。

比如变量容易被覆盖。

css 复制代码
    console.log(a)
    console.log(b)
    var a = 1
    function b() {
      console.log(a)
      var a = 2
    }
    b()

还有一个常见的坑,就是本应销毁的变量没有被销毁。

css 复制代码
   function b() {
      for(var a = 0 ;a< 7; a++){}
      console.log(a)
    }
    b()

所以在es6中,引入了let, const来实现块级作用域。(现在基本取代了var)

如何实现块级作用域?

在编译阶段,会将块级变量放到词法环境中。

比如下面这段代码

ini 复制代码
let a = 1
var b = 2
if(true) {
  let c = 3
  let d = 4
  var e = 5
}

此时的上下文如下所示。

并且当我访问变量的时候,查找的顺序,是先从词法环境开始找,再到变量环境。

ps(块级变量的创建会被提升,但是初始化和赋值不会被提升,所以如果在变量前打印是会报错的)

什么是作用域链?

在块级作用域这里说了,变量的查找顺序。

但是,当在自己的作用域范围内找到不到,该怎么办呢?

在上下文中,还有一个属性叫outer,指向需要查找的上下文,也叫做作用域链。

ps(作用域链是在代码中就确定了的,无需在编译中计算)

还是先看一段代码。

javascript 复制代码
function a() {
  console.log(aa)
}

function b() {
  var aa = "2"
  a()
}

var aa = "1"
b()

也就是说,代码写在哪里,outer就指向该上下文。

理解了作用域链,再来看闭包就简单多了。

如何理解闭包?

说闭包之前,先说两句话。

1.函数执行完毕,会销毁函数上下文,变量会被回收

2.内部函数总是可以访问其外部函数中声明的变量(通过作用越链)

这其实就是闭包形成的原因,外面的函数需要销毁,而函数里面的变量被占用,所以就形成了一个内部函数专属的内存包(只有它可以访问),也叫闭包。

javascript 复制代码
function a() {
  let a1 = "a1"
  return function b() {
    console.log(a1)
  }
}

const b = a()
b()

当执行到b = a()的时候,此时的上下文。

因为a函数已经执行完毕,但是变量a1被b函数占用,此时的上下文如下。

ps(闭包的变量回收,果是全局,则等页面销毁,如果是局部,则等函数执行完毕)


到这里,今天的文章就结束了。

总结一下,想要彻底理解变量提升,闭包,作用域链还是需要从上下文入手。

彻底理解了上下文之后,一些不符合直觉的代码,或者特性就能对号入座了。

如果这篇文章对你有所收获,欢迎点赞评论。

相关推荐
前端Hardy5 分钟前
HTML&CSS:产品卡片动画效果
前端·javascript
货拉拉技术11 分钟前
货拉拉开源:鸿蒙路由 TheRouter
android·前端·harmonyos
中杯可乐多加冰13 分钟前
工业4.0数字孪生新引擎:星图云开发者平台全景评测
前端·低代码·掘金·金石计划
云边小卖铺.19 分钟前
运行vue项目报错 errors and 0 warnings potentially fixable with the `--fix` option.
前端·javascript·vue.js
我是若尘21 分钟前
前端处理大量并发请求的实用技巧
前端
Lstmxx22 分钟前
Electron:使用数据流的形式加载本地视频
前端·electron·node.js
JunjunZ30 分钟前
unibest框架开发uniapp项目:兼容小程序问题
前端·vue.js
lyc23333332 分钟前
鸿蒙Next应用启动框架AppStartup:流程管理与性能优化🚀
前端
Data_Adventure32 分钟前
Vue 3 作用域插槽:原理剖析与高级应用
前端·vue.js
Splendid36 分钟前
Geneformer:基于Transformer的基因表达预测深度学习模型
javascript·算法