美文网首页JS存储
javascript中的堆和栈

javascript中的堆和栈

作者: miao8862 | 来源:发表于2021-05-23 15:30 被阅读0次

    这篇文章是对上次堆栈考察题的一个概念补充,我们知道,基本类型是保存在栈内存中,复杂类型保存在堆内存中

    堆和栈

    定义

    • 栈(stack):栈会自动分配内存空间,会自动释放,存放基本类型(String,Number,Boolean,Null,Undefined,BigInt),简单的数据段,占据固定大小的空间。
    • 堆(heap):动态分配的内存,大小不定也不会自动释放,存放引用类型(Function,Array,Object),指那些可能由多个值构成的对象,保存在堆内存中,包含引用类型的变量,实际上保存的不是变量本身,而是指向该对象的指针。

    区别

    • 栈:所有在方法中定义的变量都是放在栈内存中,随着方法的执行结束,这个方法的内存栈也自然销毁。

      • 优点:存取速度比堆快,仅次于直接位于CPU中的寄存器,数据可以共享;
      • 缺点:存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。
    • 堆:堆内存中的对象不会随方法的结束而销毁,即使方法结束后,这个对象还可能被另一个引用变量所引用(参数传递)。创建对象是为了反复利用,这个对象将被保存到运行时数据区。

    栈和堆的溢出

    • 栈:可以递归调用方法,这样随着栈深度的增加,JVM维持着一条长长的方法调用轨迹,知道内存不够分配,产生栈溢出。
    • 堆:循环创建对象,通俗点就是不断的new 一个对象。

    js中执行上下文的创建

    运行环境的创建

    执行上下文的创建离不开JS的运行环境,JS的运行环境包括全局环境、函数环境和eval执行部分:

    1. 在第一次载入js代码时,会首先创建一个全局环境。全局环境位于最外层,直到应用程序退出后才会被销毁。
    2. 每个函数也会有自己的运行环境,当函数被调用时,就会进入该函数的运行环境。当函数执行完毕后,该环境会被销毁。不同的函数会创建不同的环境,同一个函数被调用多次时也会创建不同的函数环境。

    执行上下文

    每当进入一个不同的环境时,都会创建一个新的执行上下文(下面称为EC),它包括:

    1. 建立作用域链(Scope chain)
      作用域链可以简单地理解为在哪执行,哪就是它的当前作用域,从当前作用域一直向上找,直到全局作用域,这条链就可以称为作用域链

    2. 创建变量对象(Variable Object, 简称VO
      在编译阶段,会创建VO,这个VO会保存当前上下文中定义的所有变量和函数声明,在执行阶段,js会对变量进行赋值,此时变量对象VO会转化活动对象AO(Active Object),转换后的AO才可被访问,这就是VO->AO的过程

    3. 确定this的指向
      this指向执行当前代码对象的所有者

      • 在全局环境中:this指向全局对象(浏览器windownodeglobal
      • 函数内部:this指向取决于当前函数的调用方式,如果是构造函数,指向的是实例对象;如果是直接调用,指向全局对象;如果是被某对象调用,指向调用它的对象
      • 在箭头函数中:指向它被创建的环境

    在下面的例子中,father的作用域链[[ scope ]]就是全局[[ scope ]],而son[[ scope ]]包括:father[[ scope ]]全局[[ scope ]]

    function father() {
      console.dir(son)
      var  x = 1;
      function son() {
        x = 2;
      }
    }
    father()
    console.dir(father)
    
    image.png

    在执行的过程我们会遇到函数或各种块级作用域,所以为了区分他们,会为它们创建不同的环境,也称为xx执行上下文EC(xx)(其中xx是对应的函数或块级),其过程产生的私有变量,包括形参、变量声明,都在放在其作用域AO(xx)

    执行过程中,变量对象VO(VARIABLE OBJECT)会变成AO(ACTIVITED OBJECT激活对象),如果在自身作用域中找不到变量,会向其父作用域上查找,如果父作用域也没有,会一级一级继续向上作用域查找,直到全局作用域,如果都没有就会返回undefined

    js中堆栈中一些要注意的事

    对象的key如果是引用值,那么在赋值时,会将key先转换成string类型,然后在赋值

    let a = {
      m: 1
    }
    let b ={
      x: 2
    }
    let obj = {}
    obj[a] = "小花"
    obj[b] = "小明"
    console.log(a)  // 小明
    
    // 实际上做以下处理:
    // 1. obj[a] => obj[a.toString()] => obj['[object object]']
    // 2. obj[b] => obj[a.toString()] => obj['[object object]']
    // 3. obj[a] == obj[b] == "小明"
    

    参考:
    https://www.cnblogs.com/xzybk/p/12528230.html

    相关文章

      网友评论

        本文标题:javascript中的堆和栈

        本文链接:https://www.haomeiwen.com/subject/yqjujltx.html