【重走JavaScript之高级程序设计】变量、作用域与内存

一、原始值和引用值

JS可以保存两种类型的值:原始值和引用值。原始值是最简单的数据,引用值则是由多个值构成的对象。变量赋值时,首先要确定这个值是原始值还是引用值。

  1. 原始值:Undefined、Null、Boolean、Number、String和Symbol。原始值保存在栈内存上。

    保存原始值的变量是按值访问的,我们操作的就是存储在变量的实际值

  2. 引用值:广义上的对象。引用值保存在堆内存上。

    保存引用值的变量是按引用访问的。JS是不允许直接访问内存地址的,所以不能直接操作对象所在的内存空间。包含引用值的变量实际上只包含指向相应对象的一个指针,而不是对象本身,操作对象时实际上操作的是对该对象的引用(指针),而非对象本身。


复制值

除了存储方式不同,原始值和引用值在通过变量复制时也有所不同

  1. 通过变量把一个原始值赋值到另一个变量,原始值会被复制到新变量的位置。
  2. 把引用值从一个变量赋给另一个变量,存储在变量中的值也会被复制到新变量所在的位置。复制的值实际上是一个指针,指向存储在堆内存中的对象。操作完成后,两个变量实际上指向同一个对象,因此会发生匪夷所思的联动。

传递参数

JS所有函数参数的传递都是按值传递,函数的参数就是布局变量

确定类型

判断原始值时使用typeof,判断引用值时使用instanceof

按照定义,所有引用值都是Object的实例,使用instanceof检测任何引用值是否为Object都会返回true,使用instanceof检测原始值,始终返回false。

站内跳转,JS类型判断完整版


二、执行上下文与作用域

任何变量(无论原始值和引用值)都存在于某个执行上下文中,这个执行上下文保存了当前的作用域(JS为静态作用域),这个上下文(作用域)决定了变量的生命周期,以及他可以访问代码的哪些部分。

每个函数调用都有自己的上下文,上下文中的代码在执行的时候,会创建变量对象的一个作用域链,JS程序是通过上下文栈进行控制流程的。

  1. 执行上下文分全局上下文、函数上下文和块级上下文。全局上下文是最外层的上下文,全局上下文的变量对象始终是作用域链的最后一层变量对象。上下文在其所有代码执行完后被销毁,全局上下文在关闭网页时才会被销毁
  2. 执行上下文和作用域的区别,JS程序按照执行上下文的顺序(作用域链)执行不断变化,但是作用域是静态不会更改的,他储存在相应的执行上下文中
  3. 函数和块级会沿着作用链逐级寻找变量,直到找到变量为止。内部上下文可以通过作用域链访问外部上下文中的一切,但外部上下文无法访问内部上下文中的任何东西,这句话等效于全局上下文(外部)只能访问全局的变量和函数,无法直接方位局部上下文中的任何东西。
  4. 函数参数被认为是当前上下文中变量,遵循上下文中的访问规则。

变量申明

var关键字是过去时的产物,let和const关键字才是首选。除非明确变量肯定会重新赋值,应尽可能的使用const变量,可以避免重新赋值的BUG

let的作用域是块级的,let存在暂时性死区,不能在let申明之前使用该变量

const申明变量时必须同时初始化为某个值,但是申明对象时,可以修改对象的键值(可以理解为对象的引用地址没有变化)。如果希望让整个对象都不能修改,可以使用Object.freeze(),const o = Object.freeze({})


三、垃圾回收

JS是使用垃圾回收的语言,不像C和C++需要手动清理,但不意味着你不需要手动清理以提高内存管理。离开作用域的值会被自动标记为可回收,在垃圾回收期间被删除。

浏览器的垃圾回收主要有两种策略,标记清理和引用计数。现在各大浏览器均采用标记清理来进行垃圾回收,标记清理:先给当前不适用的值加上标记,然后再垃圾回收期间统一回收内存。

内存管理,

  1. const和let会让变量提早进行垃圾回收。
  2. 全局对象、全局对象的属性和循环引用都应该在不需要时手动解除引用。
  3. ChormeV8 引擎会使用的相同的隐藏类进行共享,比如,
function Title(){
    this.title = 'Hello World'
}
let a1 = new Title(); let a1 = new Title()
  1. 避免构造函数属性的“先创建再补充”式的动态属性赋值,在构造函数中一次性申明所有属性
  2. 构造函数中,不要使用delete删除属性,而是将不想要的属性设置为null,这样可以保持隐藏类不变和继续共享,同时达到删除引用值和垃圾回收

内存泄漏

内存变量一直无法清除,会造成内存泄漏

为防止内存泄漏,应避免以下情况的发生

  1. 全局属性的定义
  2. 自定义事件、定时器未及时清理
  3. 闭包延长了函数内的变量生命周期
posted @ 2022-09-05 23:49  wanglei1900  阅读(28)  评论(0编辑  收藏  举报