JavaScript调用栈和作用域链指南:新手也能快速上手

作用域链和调用栈是 JavaScript 中非常重要的概念,它们负责管理函数作用域和调用关系。本文将深入探讨作用域链和调用栈的概念、原理以及在实际编程中的应用,帮助读者更好地理解这两个概念。

调用栈

在 JavaScript 中,调用栈是用来管理函数调用关系的一种数据结构。当代码执行到一个函数时,会将该函数的信息(如参数、局部变量)压入调用栈中;当函数执行完成后,会将其从调用栈中弹出。这种先进后出的数据结构使得 JavaScript 可以追踪函数的调用关系,确保代码的执行顺序和上下文的切换。

调用栈的原理

让我们通过下面的代码示例来说明调用栈的原理:

csharp 复制代码
var a = 2

function add(){
    var b = 10
    return a + b
}

add()

来看这个例子,代码执行之前要预编译(有不懂预编译的小伙伴可以去看下这篇文章 《 简单易懂!!超详细带你了解什么是JavaScript预编译!》 ) 编译器编译全局代码的时候会创建一个全局执行上下文对象,而栈就是用来存储一个又一个执行上下文对象的,此时全局执行上下文入栈了:

在全局执行上下文中有一个变量环境和词法环境,简单来说,变量环境中放的是你用var声明的变量,词法环境中放的是你用let和const声明的变量,预编译的时候就会把这些变量放进去:

全局预编译完成后,就开始执行代码,此时a得到值2:

之后执行函数add的调用,执行之前又要先进行它的编译,此时编译器又会创建一个add函数的执行上下文对象,将其压入栈:

编译后执行,b的值变为10:

还要返回a+b的值,所以就要去查找a和b,查找规则是先从本身的词法环境里找,找不到再去本身的变量环境里找,我们在本身的变量环境里找到了b = 10,但是没找到a,就去下一个执行上下文里找(此例中为全局执行上下文),同样先从词法环境里找,找不到再去变量环境里找,得到a = 2,然后就可以返回a+b的值即12,查找方向如图:

这整个红色的框框其实就是调用栈 ,我们用它来管理函数的调用关系,谁先入栈谁后入栈,如何访问a如何访问b, 调用栈能梳理清楚呈现出来。

调用栈的应用

了解调用栈的原理对于理解 JavaScript 中的函数调用、递归等具有重要意义。同时,调用栈也是 JavaScript 引擎控制执行上下文切换的重要工具,对于开发者而言,合理地利用调用栈可以避免栈溢出等问题,提高代码的执行效率。

栈溢出

栈溢出是什么呢?我们看下面的代码:

scss 复制代码
function foo(){
    foo()
}
foo()

首先预编译全局执行上下文入栈:

然后执行函数调用,进行函数预编译又将foo执行上下文入栈:

但是函数foo里面又调用了foo,于是foo又入栈:

如此循环,一个又一个foo执行上下文入栈,而调用栈的空间是有限的,所以最会导致栈溢出。

作用域链

在深入讨论作用域链之前,我们先了解一下 JavaScript 的作用域。JavaScript 中的作用域是指变量和函数的可访问性范围,它由词法环境来管理。关于作用域的详细讲解在我《写给小白的JavaScript作用域及声明提升详解》这篇文章中有。 作用域链则是描述了在执行上下文中变量查找的过程,其本质是通过词法环境来确定某作用域的外层作用域,从内向外逐级查找变量的过程。

作用域链的原理

在 JavaScript 中,每个函数都有自己的作用域,并且可以访问其外部作用域的变量。当代码在一个作用域中无法找到某个变量时,JavaScript 引擎会沿着作用域链向上查找,直至全局作用域。这种链状的查找关系就是作用域链。

让我们通过一个简单的代码示例来说明作用域链的原理:

csharp 复制代码
function bar (){
    console.log(myName);
}

function foo(){
    var myName = '涛哥'
    bar()
}

var myName = '万总'

foo()

我们先把它的调用栈画出来:

但其实调用栈的变量环境里面还有一个内置的outer属性,全局执行上下文里面的outer值为null,foo里的outer会指向全局,bar里的outer也指向全局,outer的指向是它所在的执行上下文所处的词法环境,即它所处的作用域,而foo和bar外层的作用域都为全局:

而我们执行这段代码,调用foo,foo里面又调用了bar,执行输出myName的值为'万总',查找myName,bar本身内部是没有的,之后往外去哪查找,是看outer的指向,bar的outer指向全局执行上下文,所以去那里面查找,得到myName的值为'万总'。

其实所谓的作用域链就是作用域的链状查找关系,通过词法环境来确定某作用域的外层作用域,查找变量由内而外的这种链状关系,就叫做作用域链。

作用域链的应用

了解作用域链的原理对于理解闭包、作用域的嵌套关系以及变量的查找具有重要意义。在实际编程中,合理利用作用域链可以更好地管理变量和函数,避免命名冲突,提高代码的可维护性。

调用栈和作用域链到这里就讲完啦,希望看完这篇文章后能帮助你们更好的理解这两个概念ヾ(◍°∇°◍)ノ゙

相关推荐
西陵6 分钟前
Nx带来极致的前端开发体验——借助CDD&TDD开发提效
前端·javascript·架构
叹一曲当时只道是寻常7 分钟前
vue中添加原生右键菜单
javascript·vue.js
小磊哥er17 分钟前
【前端工程化】前端工作中的业务规范有哪些
前端
旷世奇才李先生22 分钟前
Next.js 安装使用教程
开发语言·javascript·ecmascript
ᥬ 小月亮27 分钟前
webpack基础
前端·webpack
YongGit1 小时前
探索 AI + MCP 渲染前端 UI
前端·后端·node.js
慧一居士1 小时前
<script setup>中的setup作用以及和不带的区别对比
前端
RainbowSea2 小时前
NVM 切换 Node 版本工具的超详细安装说明
java·前端
读书点滴2 小时前
笨方法学python -练习14
java·前端·python
Mintopia2 小时前
四叉树:二维空间的 “智能分区管理员”
前端·javascript·计算机图形学