第4章 —— 变量、作用域与内存

发布时间 2023-03-26 15:22:01作者: Bookisso

4.1 原始值和引用值

原始值是最简单的数据,引用值是存储在内存中的对象。保存原始值的变量是按值访问的,引用值的变量是按引用访问的。

区别:

  • 原始值大小固定,保存在栈内存上;引用值是对象,保存在堆内存上。
  • 原始值不能添加属性,只有引用值可以动态添加属性。
  • 原始值的初始化可以只使用字面量形式;如果使用new,会创建一个对象的实例。
  • 原始值的复制会被复制到新的位置;引用值复制的是一个指针,与原来的指针指向内存中的同一个对象。

相同点:

  • 传递参数时都是按值传递!

  对引用值来说可以理解为在函数内部复制了一个指向原对象的指针,不影响函数外部原指针的指向。

  Ps:按值传递指外部的值会被复制到一个局部变量;按引用传递指值在内存中的位置会被保存在在一个局部变量,对本地变量的修改会反映到函数外部。

那么如何确定值的类型呢?

Typeof可以很好得判断值为原始类型还是引用类型,但他有两个缺点:

  1. typeof null === ‘object’
  2. 不能很好的判断时什么类型的对象

  基于上述缺点,ECMAScript提供了intanceof操作符,可以使变量沿着原型链查找自己是哪个具体类型的实例。

  哦对,typeof function === ‘function’,这是因为ECMA-262中规定,任何实现内部[[Call]]方法的对象都应该返回‘function’。

4.2 执行上下文和作用域

4.2.1 定义

上下文决定当前函数可以访问到哪些数据,每一个上下文都对应着一个变量对象,这个上下文中所定义的所有函数和变量都保存在这个对象上。

全局上下文对应的全局对象,也就是我们常说的window对象,所以我们定义的全局变量和函数,都是window对象的属性和方法。

当上下文中的代码在执行时,会创建变量对象的一个作用域链,它决定着代码在访问变量和方法时的顺序

代码正在执行的上下文对应的变量对象就是当前的活动对象,活动对象最初只有arguments一个变量(window对象没有)。

作用域链的最前端是当前执行上下文所对应的变量对象,下一个对象则是来自包含上下文,以此类推,直至全局上下文;作用域链的最后一个对象就是全局对象。

4.2.2 作用域链增强

指代码在执行到某些语句时,会在作用域链前端临时新增一个变量对象,在语句执行后该变量会被删除。

这类语句包括:try/catch中的catch语句和with语句。

相当于执行到这些语句时,语句内部自己独立成了一个上下文,从外部无法访问里面的变量和方法。

4.2.3 变量声明

创建上下文后,首先先为上下文对应的变量对象设置属性,也就是先看声明了哪些变量或函数,设置的顺序如下:

  1. 先创建arguments对象,并根据传入的实参为arguments对象设置属性和属性值;如果没有传入实参,则属性值为undefined;
  2. 执行所有函数声明,如果函数名与arguments属性有所冲突,则会覆盖arguments;
  3.  执行所有变量声明,如果与arguments有所冲突,则忽略该声明;
  4. 所有的var语句都放到最前面声明,至于var函数和var变量,规则和2、3一样;
  5. 只有arguments对象会在初始时复制,其他在声明后都赋值为undefined,到执行时,也就是变为活动对象时才能访问具体的属性值。

4.3 垃圾回收

JS垃圾回收主要用到两种回收策略:标记清除和引用计数。

标记清除:首先会标记内存中的所有变量,然后将还在执行上下文中的变量的标记去掉,清除掉还带有标记的变量。

引用计数:声明变量时给变量赋一个引用值为1,之后有将它赋给其他变量时,它的引用数+1,被覆盖时引用数-1。但会出现循环引用问题,导致大量内存没有被释放,甚至出现内存泄漏的问题。

4.4 内存管理与泄漏

内存优化方式:

  1. 手动解除引用,把不再使用的变量设为null,下一次垃圾清理时就会把它清除掉;
  2. 使用const和let,这个在块作用域比函数作用域早终止时有用;
  3. 浏览器会用隐藏类,可以利用此特性先创建再补充

内存泄漏:

内存泄漏是指不再使用的内存最终却未能释放,造成的原因主要有以下几种:

  1. 没有声明直接赋值,则变量变为一个全局变量;
  2. 错误的使用闭包;
  3. 定时器没有取消;
  4. DOM清空或删除后,事件未清除。

 

之前其实已经有很详细地看过这一章,但今天再看有发现了很多之前没有注意到的小细节,比如隐藏类这种,特别是这次才弄懂内存泄露到底是什么意思!

之前一直以为内存泄漏是安全问题,指其他人可以从外部访问变量,这次再看才知道指的竟然是内存占用。