# JS数据类型

  • 最新的 ECMAScript 标准定义了 8 种数据类型:
  • 7 种原始类型:
    • Null, Undefined, Number,Boolean, String, BigInt, Symbol(符号类型)
    • 所有基本类型的值都是不可改变的。但需要注意的是,基本类型本身和一个赋值为基本类型的变量的区别。变量会被赋予一个新值,而原值不能像数组、对象以及函数那样被改变
    • 基本类型值可以被替换,但不能被改变
  • 第8种 Object 类型又具体包含了 function、array等

# js内存管理

  • https://blog.csdn.net/sinat_23515817/article/details/86550612
  • 基本类型的值存储在栈中,栈结构的特点是后进先出,入栈、出栈都在栈顶进行,栈一般用于保存固定大小的值,通过访问保存值的变量直接可以操作值,所以说基本类型是按值访问的,
  • 引用类型的值存储在堆中,堆是没有结构的,用于为一些数据大小不确定的复杂类型(引用类型)数据分配空间,例如对象、数组,JS不允许直接访问堆内存中的位置,因此我们不能直接操作对象的堆内存空间。JS提供了一种间接的方式,将对象在堆中的引用(地址)保存到变量中,使用变量时,就间接地通过引用(地址)访问对象属性。因此,引用类型的值都是按引用访问的。
  • 原始值。存储在栈(stack),即它们的值直接存储在变量访问的位置
  • 引用值。 存储在堆(heap)中的对象,存储在变量处的值是一个指针,指向存储对象的内存处,
  • 原始类型占据的空间固定,将它们存储在较小的内存区域,栈,便于迅速查寻变量的值
  • 引用类型的值,它的存储空间从堆中分配,因为引用值的大小会改变,故不能存放在栈中,这样会降低变量查寻的速度,而将该对象存储在堆中地址存放于变量的栈空间,地址大小固定,这样对变量性能无任何影响

# 内存生命周期

  • 分配内存
    • 当我们声明变量、函数,并初始化时(未初始化,默认值undefined),JS引擎在内存中自动为它们开辟空间
  • 使用内存
    • 复制变量值,在从一个变量向另一个变量复制值的时候,基本类型复制的是值,而引用类型复制的是引用地址。
    • 应用——函数参数传递,JavaScript函数参数的传递,其实就是复制变量值的应用。向参数传递基本类型的值,被传递的值会复制给一个局部变量;向参数传递引用类型的值时,会把这个值在内存中的地址复制给一个局部变量,因此这个局部变量的变化会反应在函数的外部。
    • 参数为基本类型时,函数体内复制了一份参数值,对于任何操作不会影响参数实际值
    • 函数参数是一个引用类型时,当在函数体内修改这个值的某个属性值时,将会对参数进行修改
    • 函数参数是一个引用类型时,如果我们直接修改了这个值的引用地址,则相当于函数体内新创建了一份引用,对于任何操作不会影响原参数实际值
  • 释放内存,垃圾回收算法
    • 引用计数
      • 在内存管理的环境中,一个对象如果有访问另一个对象的权限(隐式或者显式),叫做一个对象引用另一个对象。例如,一个Javascript对象具有对它原型的引用(隐式引用)和对它属性的引用(显式引用)
      • 此算法把“对象是否不再需要”简化定义为“对象有没有其他对象引用到它”。如果没有引用指向该对象(零引用),对象将被垃圾回收机制回收
      • 限制:无法处理循环引用的事例
    • 标记清除
      • 这个算法把“对象是否不再需要”简化定义为“对象是否可以获得
      • 垃圾回收器将定期从全局对象开始,找所有从全局对象开始引用的对象,然后找这些对象引用的对象……从根开始,垃圾回收器将找到所有可以获得的对象和收集所有不能获得的对象
      • 因为“有零引用的对象”总是不可获得的,但是相反却不一定,循环引用不再是问题了
      • 限制: 那些无法从根对象查询到的对象都将被清除,但实践中我们很少会碰到类似的情况

# 内存泄露

  • 内存泄漏(memory leak)是指程序不再使用的内存,由于某些原因,无法被释放到内存中。虽然JavaScript有自动垃圾收集,但是如果我们的代码写法不当,会让变量一直处于“进入环境”的状态,无法被回收,造成内存泄漏

# 常见的四种内存泄露

  • 全局变量
  • 被遗忘的计时器和回调
  • 闭包
  • DOM 之外的引用

# JS类型判断

  • typeof
    • typeof 可以准确判断出除 null 以外的基本类型,以及 function 类型,object类型, null 会被 typeof 判断为 object
  • instanceof
  • Object.prototype.toString.call(o).slice(8,-1)