zoukankan      html  css  js  c++  java
  • 诡异的 javascript 变量

    诡异例子:

    function DelayExe() {

      var a = 10;

      setTimeout( function Print() { console.log(a); },  1000  );

    }

    以C++的观点来看:一旦函数DelayExe()执行完毕,变量 a 就不复存在,函数 Print() 根本引用不到变量a,必然抛出异常。

    但是,在 js 中,在 DelayExe() 执行完毕 1000ms 后执行 Print() 函数,Print() 函数居然能正确引用 a。是不是很诡异?

      

    概念和原理:

    举个例子:

    一个64位整型变量,在 C++ 程序中,只占8字节。

     一个64位整型变量,在 js 中,占有以下空间:

    M1:8字节,表达数值

    M2:引用计数器,若是 5,表示有 5 个函数引用该变量。

    每当一个函数引用该变量时,js 引擎就递增变量的引用计数器

    每当一个函数用完该变量后,js 引擎就递减变量的引用计数器。若变量的引用计数器递减为 0,说明已没有函数用到该变量,该变量才会被删除。

    由此可见:

    C1:js 中的变量,不仅包含数值,还包含引用计数器。

    C2:变量是否存在,取决于其引用计数器的值。

    C3:js 引擎根据引用计数器的值决定是否删除变量,是否从内存中清除变量。

    实际用例:

    function DelayExe() {

      var a = 10;

      setTimeout( function Print() { console.log(a); },  1000  );

    }

    以C++的观点来看:

    变量 a 是函数DelayExe() 的局部变量,函数 DelayExe() 执行完毕,变量 a 就被删除,不再占用存储空间。

    但是,js 的情况截然不同:

    S1:执行函数 DelayExe()

    S2:执行到语句 setTimeout() 时,做如下处理:

    S2.1:在内存中分配一块空间,存储函数 Print(),

    S2.2:由于函数 Print() 引用了变量 a,递增变量 a 的引用计数器

    S2.3:安排函数 Print() 在 1000ms 后被放入任务队列调度执行

    S3:函数 DelayExe() 执行完毕。由于此时变量 a 的引用计数器 > 0,所以 js 引擎不将其不删除,变量 a 依然存在。

    S4:1000ms 后执行函数 Print(),执行完毕后,函数 Print() 不再引用变量 a,js 引擎递减变量 a 的引用计数器。此时,变量 a 的引用计数器递减为 0,不再被任何函数引用,于是 js 引擎才将其从内存中删除。

     

    结论:

    C1:js 变量的存在性,与变量的定义位置无关。

    C2:js 变量是否存在,是否占用存储空间,取决于有没有被函数引用。

    C2.1:若没有被任何函数引用,变量就被删除。

    C2.2:若至少被一个函数引用,变量就存在。

    C3:若一个变量被一个函数引用,但这个函数一直未被调用执行,那么这个变量就一直存在。

  • 相关阅读:
    Java基础——数组复习
    JavaScript 笔记总结
    HTML总结
    css代码添加背景图片常用代码
    Java 并发包之线程池综述
    Java 并发之Concurrent 包综述
    AQS 框架之 Lock 接口
    AQS 框架之 LockSupport 线程阻塞工具类
    JAVA 锁之 Synchronied
    ThreadLocal 线程本地变量
  • 原文地址:https://www.cnblogs.com/micemik/p/15765872.html
Copyright © 2011-2022 走看看