资讯专栏INFORMATION COLUMN

浅析javascript调用栈

diabloneo / 1778人阅读

摘要:调用栈是一种栈结构它用来存储计算机程序执行时候其活跃子程序的信息。调用栈是解析器的一种机制。并形成一个栈帧任何被这个函数调用的函数会进一步添加到调用栈中,形成另一个栈帧并且运行到它们被上个程序调用的位置。然后调用栈继续运行其他部门。

大家在进行javascript开发的时候,有没有想过,我们写的代码是怎么样运行的呢?下面我们就来剖析一下代码的执行过程。

一 什么是调用栈

代码在运行过程中,会有一个叫做调用栈(call stack)的概念。调用栈是一种栈结构,它用来存储计算机程序执行时候其活跃子程序的信息。(比如什么函数正在执行,什么函数正在被这个函数调用等等信息)。调用栈是解析器的一种机制。call stack

我们以一段简单代码为示例,来看一看到底什么是调用栈,它是一个怎么样的运行机制

 function boo (a) {
    return a * 3
  }
  function foo (b) {
    return boo(4) * 2
  }
  console.log(foo(3))
二 详解代码执行

下面我们来分析一下上述代码的执行过程
(1)console.log(foo(3)) 执行,形成一个栈帧,调用foo函数,再形成另一个栈帧。
(2)新的栈帧压在上一个栈帧之上,继续执行代码,foo函数中又调用了boo函数,形成了另一个栈帧压在旧栈帧之上。然后执行boo。
(3)当执行完boo时候,返回值给foo函数之后,boo被推出调用栈,foo函数继续执行,然后foo函数执行完,返回值给console.log,foo函数被推出调用栈,console.log得到foo函数的返回值,运行,输出结果,最后console.log也被推出调用栈,该段程序执行完成。
图解代码运行过程:

三 一个更复杂的例子
// 省略一部分html

$.on("button", "click", function onClick() {
    setTimeout(function timer() {
        console.log("You clicked the button!");    
    }, 0);
});

console.log("Hi!");

setTimeout(function timeout() {
    console.log("Click the button!");
}, 5000);

console.log("My Name Is Chirs.")

大家看看上叙的代码,结合一下前面的的分析,思考一下调用栈是怎么工作的?
(1)先运行绑定事件函数,把onClick事件绑定在button标签上。该函数没有没有调用其他函数。
(2)接下来运行console.log("hi"),该函数没有调用任何其他函数。
(3)然后继续执行下面的setTimeout,setTimeout是一个异步函数,经过5秒之后,在运行队列里面插入这个回调函数,然后如果该队列之前没有其他函数,就执行该队列,有则等待前面的函数执行完成,再执行。
(4)console.log("My Name Is Chirs")不会等待5s之后,再执行,因为settimeout并不会在调用栈中执行5秒,实际上它在调用栈中是立即执行完的。
(5)假设在这个时候,我们点击了按钮,按钮绑定的回调事件被添加到运行队列中。(运行队列中的代码要等调用栈被清空之后才会执行)由于调用栈中还有代码需要执行,所以会继续执行下面的console.log()
(6)然后执行完console.log之后,由于时间还没有经过5s,所以点击的回调事件会被先压入栈中去执行,由于该回调事件里面又是一个settimeout事件,由于它的事件间隔只有0s,所以这个settimeout的回调会先被压入运行队列。先输出You clicked the button! 再过几秒之后,间隔为5s的settimeout把回调函数压入队列,这时候调用栈中没有代码在执行,所以会执行这个代码,输出"Click the button“。结束代码运行。

同样来看一个运行示意图:

四 总结

调用栈其实就是一种解析器去处理程序的机制,它是栈数据结构。它能追踪子程序的运行状态。
(1)当脚本要调用一个函数时,解析器把该函数添加到栈中并且执行这个函数。并形成一个栈帧
(2)任何被这个函数调用的函数会进一步添加到调用栈中,形成另一个栈帧,并且运行到它们被上个程序调用的位置。
(3)当执行完这个函数后,如果它没有调用其他函数,则它会从调用栈中推出。然后调用栈继续运行其他部门。
(4) 异步函数的回调函数一般都会被添加到运行队列里面,如settimeout会在响应的时间后把回调函数放入队列中,队列里的函数需要等栈为空时才会被推入栈中执行。如果队列中有其他函数,需要等队列前面的函数被堆入调用栈中之后才会运行。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/84337.html

相关文章

  • 浅析 JS 中的 EventLoop 事件循环(新手向)

    摘要:同时,如果执行的过程中发现其他函数,继续入栈然后执行。上面我们讨论的其实都是同步代码,代码在运行的时候只用调用栈解释就可以了。 序 Event Loop 这个概念相信大家或多或少都了解过,但是有一次被一个小伙伴问到它具体的原理的时候,感觉自己只知道个大概印象,于是计划着写一篇文章,用输出倒逼输入,让自己重新学习这个概念,同时也能帮助更多的人理解它~ 概念 JavaScript 是一门 ...

    chadLi 评论0 收藏0
  • 浅析 event-loop 事件轮询

    摘要:如果执行的准备时间大于了,因为执行同步代码后,定时器的回调已经被放入队列,所以会先执行队列。 showImg(https://segmentfault.com/img/remote/1460000018998584); 阅读原文 浏览器中的事件轮询 JavaScript 是一门单线程语言,之所以说是单线程,是因为在浏览器中,如果是多线程,并且两个线程同时操作了同一个 Dom 元素,...

    2501207950 评论0 收藏0
  • 浅析JavaScript异步

    摘要:回调函数,一般在同步情境下是最后执行的,而在异步情境下有可能不执行,因为事件没有被触发或者条件不满足。同步方式请求异步同步请求当请求开始发送时,浏览器事件线程通知主线程,让线程发送数据请求,主线程收到 一直以来都知道JavaScript是一门单线程语言,在笔试过程中不断的遇到一些输出结果的问题,考量的是对异步编程掌握情况。一般被问到异步的时候脑子里第一反应就是Ajax,setTimse...

    Tangpj 评论0 收藏0
  • 浅析 Vue 2.6 中的 nextTick 方法

    摘要:核心的异步延迟函数,用于异步延迟调用函数优先使用原生原本支持更广,但在的中,触摸事件处理程序中触发会产生严重错误的,回调被推入队列但是队列可能不会如期执行。 浅析 Vue 2.6 中的 nextTick 方法。 事件循环 JS 的 事件循环 和 任务队列 其实是理解 nextTick 概念的关键。这个网上其实有很多优质的文章做了详细介绍,我就简单过过了。 以下内容适用于浏览器端 JS,...

    fobnn 评论0 收藏0
  • 浅析JS中的堆内存与内存

    摘要:如,不是定义常量么为什么还能改这就是我们今天要说的重点中的堆内存与栈内存在引擎中对变量的存储主要有两种位置,堆内存和栈内存。而堆内存首先要在堆内存新分配存储区域,之后又要把指针存储到栈内存中,效率相对就要低一些了。 最近跟着组里的大佬面试碰到这么一个问题, Q:说说var、let、const的区别A:balabalabalabla...Q:const定义的值能改么?A:你逗我?不能吧 ...

    Faremax 评论0 收藏0

发表评论

0条评论

diabloneo

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<