• JavaScript变量存储浅析


    var test=100;
    function foo(){
        console.log(test);//undefined
        var test=200;
        console.log(test);//200
    }

    我们明明定义了一个全局变量test,按照JS作用域链的理论,func应该可以访问到全部变量test的啊?

    是的,按照作用域链的思想,func函数在运行时,在其局部变量内找不到test变量的话,理应向上在全部作用局中继续查找。

    问题就出在函数内部的 var test =200; 这句局部变量定义。

    我们都知道JS变量类型是松散型,松散型的意思并不是说JS变量就没有变量类型,而是其变量类型是在运行时才进行确定。

    var str;
    
    str=2015;

    第一行我们定义了一个变量str,但是并未赋值,这时候JS并不知道str变量的类型,等到脚本运行到第3行,我们给str变量赋了一个值:2015.

      这时候JS才知道,哦,原来str的值是2015,这不就是Number类型吗,这才确定了str的类型。

      这让我想起了JS的另一个概念,叫作函数声明提升!

     var test = 100;
    
    func();
       
      function func(){
          console.log(test);
          var test=200;
          console.log(test);
     }
      

    将函数的声明放在了最后,但是代码仍然可以正常运行,并不会出现func未定义的错误。

      函数声明提升就说明JS在运行之前还会经历另外一个过程:预加载。(有些地方也叫作预编译)

      在预加载阶段,JS主要对全局作用域、函数的运行环境以及作用域链等进行准备,

      这里的函数运行环境就是指:读取变量定义并确定其属于哪个作用域,但不会为其赋值!

       在预加载阶段,第一行的时候定义了一个全部变量a,然后到了第7行,又给func定义了一个局部变量a,

      注意这个时候变量并未赋值,值均为undefined

      到了运行阶段:

      第一行给全部变量test赋了值:100,等到执行func函数的时候,

      在第6行,需要使用test变量,JS当然是先查找func的局部变量了,没错,预加载阶段已经为func定义了一个局部变量test,

      所以JS当然不会继续往全局进行查找了,但是使用的时候才发现居然没有值,也就是undefined!

      等到第7行JS才给局部变量test赋值。

      小结:

        JS分为预加载和执行期两个阶段,前者只会确定变量的作用域,在执行期才会对齐进行赋值,同时也就确定了变量的具体类型。

     大家都知道,JavaScript中的变量类型分为两种,一种是基本数据类型,包括:undefined,null,Number,String,Boolean,另外一种就是对象。

      两种数据类型的存储方式在JS中也有所不同。

      另外,内存分为栈区(stack)和堆区(heap),然后在JS中开发人员并不能直接操作堆区,堆区数据由JS引擎操作完成,那这二者在存储数据上到底有什么区别呢?

      我们简单的通过下面这张图来分析:

      

      JS中变量的定义在内存中包括三个部分:

    • 变量标示  (比如上图中的Str,变量标示存储在内存的栈区)
    • 变量值     (比如上面中的Str的值souvenir或者是obj1对象的指向堆区地址,这个值也是存储在栈区)
    • 对象        (比如上图中的对象1或者对象2,对象存储在堆区)

      也就是说,对于基本数据类型来说,只使用了内存的栈区。

    在JS预加载阶段,JS引擎只是在内存的栈区为每个变量分配了内存,指定了标示符,并未为其指定值。

      等到JS执行期才会为其赋值。

     1 var a=100;
     2 var obj1={
     3     attr:'hello'
     4 };
     5 
     6 func(a,obj1);
     7 
     8 function func(num,obj){
     9     var a2=num;
    10     a2=200;
    11     
    12     var obj2=obj;
    13     obj2.attr='hello';
    14 }
    15 
    16 console.log(a);
    17 console.log(obj1.attr);

     对于基本数据类型,在执行第9行时,JS是把num在栈区的值,也就是100复制给了a2这个局部变量,然后在第10行又修改了a2的值,

      这个操作过程并未影响到全局变量a的值。

      小结:

      对于对象来说,当JS执行12行的时候,实际上是把obj在栈区的值,也就是obj对象在堆区的引用地址,复制给了新的局部变量obj2,

      这时候,obj2与obj实际上已经指向了同一个堆区的对象,然后obj2修改了这个对象的某个属性值。然后函数执行完毕,obj2这个局部变量没有引用将会被GC回收。

      再次访问obj1这个全局变量时,其所指向的对象其实已经被修改过了。

    http://www.cnblogs.com/souvenir/p/4969565.html

  • 相关阅读:
    MySQL Binlog解析(2)
    在线修改GTID模式
    官方online ddl
    pt-osc原理
    pt-osc使用方法
    python基本数据类型
    第一句python
    搭建私有云kodexplorer
    frp搭建
    Linux下快速分析DUMP文件
  • 原文地址:https://www.cnblogs.com/dehuachenyunfei/p/6825473.html
Copyright © 2020-2023  润新知