JS的执行上下文

在学习JS的数据结构与算法的时候,学到二叉搜索树的先序遍历代码时,看得一脸懵逼,弹幕说这是栈什么的,根本不知道什么意思,为什么函数自己就往回走

//4、先序遍历
      BinarySerachTree.prototype.preOrderTraversal = function () {
        this.preOrderTraversalNode(this.root)
      }

      BinarySerachTree.prototype.preOrderTraversalNode = function (node) {
        if (node != null) {
          callBack(node.key)
          //左走
          this.preOrderTraversalNode(node.left)

          //右走
          this.preOrderTraversalNode(node.right)
        } 
      }

标橙是我懵逼的两段代码。按我的理解,当走到树最左端最小值的时候,由于node.left是空所以传值应该是null,函数自动停止才对。

如果没有右走代码,上面的逻辑确实没错。但是添加了右走代码,函数就会自己往回走,我很纳闷这是为什么?

 

查看公众号文章,大概明白一些逻辑:

https://mp.weixin.qq.com/s/TAZax-B-llfVXgZyKYJ_VQ?

https://www.cnblogs.com/bax-life/p/7499513.html

 

首先要理解什么是执行上下文(Execution Context)?

当 JavaScript 代码在运行的时候, 它所在的执行环境通常认为是以下其中之一:

  • Global code – 默认环境,你的代码首次执行的地方。

  • Function code – 当代码执行进入到函数体当中。

  • Eval code – 在 eval 函数内部执行的文本。

有且只能有1个全局上下文, 并且可以被程序中其他的上下文访问到。

可以有很多个函数上下文, 每个函数调用都创造一个新的上下文, 并创建出一个局部作用域,任何在作用域内部声明的东西都不能被当前函数作用域外部访问到。(闭包)

 

执行上下文栈(Execution Context Stack)

在浏览器中的 JavaScript 解释器是单线程的。这实际上意味着,在浏览器中一次只会发生一件事,其他行为或者事件在所谓的执行栈中排队等待。

浏览器第一次加载脚本, 它将默认进入全局执行上下文中。

如果在全局环境中调用了一个函数, 你的程序序列流会进入被调用的函数的当中,创建一个新的 执行上下文 并且将这个上下文压入执行栈之中。

如果你在当前函数里面又调用了另外一个函数, 也会发生同样的事情:

代码的执行流进入内部函数,这将创建一个新的执行上下文,它被压入现有栈的顶部。浏览器永远会执行当前栈中顶部的执行上下文 一旦函数在当前执行上下文执行完毕,它会被从栈的顶部弹出,然后将控制权移交给当前栈的下一个上下文当中。

(function foo(i) {
    if (i === 3) {
        return;
    }
    else {
        foo(++i);
    }
}(0));

  

 

 

这段代码调用自己自身3次, 每次将 i 的值增加 1。

每次函数 foo 被调用的时候, 就会创建一个新的执行上下文。

一旦上下文执行完毕之后, 它就会从栈中弹出并且返回控制权到下一个上下文当中,直到全局上下文又再次被访问。

 

所以:以先序遍历来说,其执行过程也如此

//4、先序遍历
      BinarySerachTree.prototype.preOrderTraversal = function () {
        this.preOrderTraversalNode(this.root)
      }

      BinarySerachTree.prototype.preOrderTraversalNode = function (node) {
        if (node != null) {
          callBack(node.key)
          //左走,一旦执行完毕,也就是node.left==null,则弹出,往回走
          //然后从之前中断的地方,也就是下面这行代码,往下走,不执行这一句了。
          this.preOrderTraversalNode(node.left)

          //右走
          this.preOrderTraversalNode(node.right)
        } 
      }

最主要的一点,就是从之前中断的地方往下走,不会死循环

 

再举一个例子,二叉树的搜索,其实也和插入差不多

      BinarySerachTree.prototype.search = function (key) {
        let node = new Node(key)
        return this.searchNode(this.root, node)
      }

      //内部调用递归搜索
      //单纯的return true和false是只会退出当前的上下文,并不会直接退到global
      BinarySerachTree.prototype.searchNode = function (node, newNode) {
        if (node != null) {
          if (newNode.key === node.key) {
            return true
          } else if (newNode.key < node.key) {
            //如果不return, 会保留当前上下文,最后search返回的是undefined
            // this.searchNode(node.left, newNode)
            return this.searchNode(node.left, newNode)
          } else if (newNode.key > node.key) {
            // this.searchNode(node.right, newNode)
            return this.searchNode(node.right, newNode)
          }
        } else {
          return false
        }
      }

 

posted @ 2022-01-10 20:06  Jacky02  阅读(137)  评论(0)    收藏  举报