JS变量、作用域和内存问题

变量

  • 基本类型值:简单数据段
  • 引用类型值:可能由多个值构成的对象

JavaScript不允许直接访问内存中的位置,不能直接操作对象的内存空间。

属性

只能给引用类型值动态地添加属性。

复制

  • 基本类型值:会在变量对象上创建一个新值

  • 引用类型值:本质上都是指针,两个指针引用同一个对象

传递参数

所有函数的参数都是按值传递的。

1
2
3
4
5
6
7
8
9
function setName(obj){
obj.name = 'Sam';
obj = new Object();
obj.name = 'Tom';
}

const person = new Object();
setName(person);
console.log(person.name); // 'Sam'

检测类型

  • typeof:检测基本类型
  • instanceof:检测引用类型,根据它的原型链来识别
  • Object.prototype.toString()

执行环境及作用域

每个函数都有自己的执行环境,当执行流进入一个函数时,该函数的环境就会推入一个环境栈中。

当代码在一个环境中执行时,会创建变量对象的一个作用域链(保证对执行环境有权访问的所有变量和函数的有序访问)。

全局执行环境的变量对象始终都是作用域链的最后一个对象。

标识符解析就是沿着作用域链一级一级地搜索标识符的过程,搜索过程始终从作用域链的前端开始,然后逐级向后回溯,直到找到标识符为止。

内部环境可以通过作用域链访问所有的外部环境,但外部环境不能访问内部环境中的任何变量和函数。

延长作用域链

  • try-catch中的catch
  • with

没有块级作用域(var)

  • 在函数内部,最接近的环境就是函数的局部环境
  • 查询标识符:搜索过程从作用域链的前端开始,向上逐级查询与给定名字匹配的标识符

垃圾收集

  1. 标记清除:垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记,接着会去掉环境中的变量以及被环境中变量引用的变量的标记。最后,垃圾收集器完成内存清除工作,销毁带有标记的值并回收所占用的内存空间

  2. 引用计数:跟踪记录每个值被引用的次数,当这个值为0,则回收

    问题:循环引用,对象A包含对象B的指针,对象B包含对象A的指针

    1
    2
    3
    4
    5
    6
    function problem(){
    const objA = new Object();
    consy objB = new Object();
    objA.some = objB;
    objB.any = objA;
    }

管理内存

解除引用:一旦数据不再使用,最好通过将其值设置为null来释放引用

解除一个值的引用并不意味着自动挥手该值所占用的内存。接触引用的真正作用是让值脱离执行环境,以便垃圾收集器下次运行时将其回收。

小结

  1. 变量分两种类型:基本类型值和引用类型值
    • 基本类型:Undefined,Null,Boolean,Number,String
    • 基本类型值在内存中占据固定大小,保存在栈内存中
    • 复制基本变量会创建值的副本
    • 引用类型的值是对象,保存在堆内存中
    • 引用类型值并不包含对象本身,是指向该对象的一个指针
    • 复制引用类型实则复制指针,两个变量都指向同一个对象
    • typeof、instanceof、Object.prototype.toString()
  2. 所有变量都存在一个执行环境(作用域)中,决定了变量的生命周期,以及哪部分代码可以访问
    • 执行环境有全局执行环境和函数执行环境之分
    • 作用域链:用于搜索变量和函数
    • 局部环境可以访问父环境,反之则不行
    • 变量的执行环境有助于确定何时释放内存
  3. 垃圾收集
    • “标记清除”是主流的垃圾收集算法:给当前不使用的值加上标记,然后回收其内存
    • ”引用计数“:跟踪记录所有值被引用的次数
0%