原始值与引用值
- 变量可以包含两种不同类型的数据
(1)原始值:最简单的数据。指Undefined Null Boolean Number String Symbol 保存原始值的变量按值访问
(2)引用值:由多个值构成的对象 指保存在内存中的对象 保存引用值的变量按引用访问 - 如果是使用new关键字 则JS会创建一个object类型的实例 但其行为(?)类似原始值
- 所有函数的参数都是按值传递 是局部变量
- typeof用于检测原始值和function
在safari5 chrome7以前 由于实现细节的原因 typeof在检测正则表达式时 也会function 因为ECMA-262规定 任何实现内部[[Call]]方法的对象都应该在typeof检测时返回function 而上述浏览器中的正则表达式实现了这个方法 所以typeof对正则表达式也返回function
执行上下文与作用域
- 每个上下文都有一个关联的变量对象 而这个上下文中定义的所有变量和函数都存在于这个对象上
- 全局上下文就是window对象
- 每个函数调用都有自己的上下文 当代码执行流进入函数时 函数的上下文被推到一个上下文栈上 在函数执行完之后 上下文栈会弹出该函数上下文 将控制权返还给之前的执行上下文
- 作用域链决定了各级上下文中的代码在访问变量和函数时的顺序
- 如果上下文是函数 则其活动对象用作变量对象 活动对象最初只有一个定义变量arguments(全局上下文中没有这个变量)
- let声明的变量在JS运行时也会被提升 但由于暂时性死区的缘故 不能在声明之前使用let变量
垃圾回收
- 执行环境负责在代码执行时管理内存
- 垃圾回收基本思路:确定哪个变量不会再使用 然后释放它占用的内存。
- 垃圾回收这个过程是周期性的 即垃圾回收程序每隔一段时间(或者说在代码执行过程中某个预定的收集时间)就会自动运行
- 垃圾回收程序必须跟踪记录哪个变量还会使用 哪个变量不再使用 以便回收内存 如何标记未使用的变量:标记清理和引用计数
- 标记清理(最常用):垃圾回收程序运行时 会标记内存中存储的所有变量(标记过程并不重要) 然后 去掉所有在上下文中的变量 以及被在上下文中的变量引用的变量的标记 在此之后 再被加上标记的变量就是待删除的了 原因是任何在上下文中的变量都访问不到他们了 随后垃圾回收程序做一次内存清理 销毁带标记的所有值并收回他们的内存
- 引用计数:记录每个变量被引用的次数。声明变量并给他赋一个引用值 这个引用值的引用数为1 如果同一个引用值又被赋给另一个变量 那么引用数+1 如果保存对该引用值引用的变量被其他值给覆盖了 那么引用数-1 当一个引用值的引用数为0时 就说明没办法再访问到这个值了 因此可以安全的收回其内存了 垃圾回收程序下次运行时就会释放引用数为0的值的内存
缺陷:循环引用。对象A有一个指针指向对象B 对象B也引用了对象A 这两个对象的引用数都不会为0
- 垃圾回收程序会周期性运行 运行时机都是根据已分配对象的大小和数量来判断
- 如果垃圾回收程序回收的内存不到已分配的15% 这些变量 字面量或数组槽位的阈值就会翻倍 如果有一次回收的内存达到已分配的85% 则阈值重置为默认值。也就是说 根据声明周期内存在的变量多少来动态改变触发垃圾回收机制的阈值 存在的变量越多 说明的确需要那么多变量 相应的阈值需要设置高一些 减少垃圾回收的运行频率 相反则降低阈值 尽早触发垃圾回收
- 将内存保持在一个较小的值可以让页面性能更好 最佳手段就是保证在执行代码时只保存必要的数据 把不用的数据设置为null 释放其引用 这也叫解除引用
- 接触引用的关键在于确保相关的值已经不在上下文中 因此他在下次垃圾回收时会被回收
- const let都以块为作用域 所以相比于使用var 使用这两个新关键字可能会更早的让垃圾回收程序接入 今早回收应该回收的内存
function Article() {
this.title = '红宝书';
}
let a1 = new Article();
let a2 = new Article();
因为这两个实例共享同一个构造函数和原型 所以V8在后台配置 让这两个类实例共享相同的隐藏类 假设之后又添加了下面这行代码:a2.author = 'Jake';
此时a1 a2就会对应两个不同的隐藏类 这样会影响性能 所以尽量避免JS的“先创建再补充”式的动态属性赋值 并在构造函数中一次性声明所有属性
function Article() {
this.title = '红宝书';
this.author = 'Jake';
}
let a1 = new Article();
let a2 = new Article();
delete a1.author;
代码运行结束后 即使a1 a2使用了同一个构造函数 他们也不再共享一个隐藏类 因为动态删除属性与动态添加属性导致的后果一样 最佳做法是
function Article() {
this.title = '红宝书';
this.author = 'Jake';
}
let a1 = new Article();
let a2 = new Article();
a1.author = null;
- 内存泄漏:意外的全局变量 即没有使用任何关键字声明的变量、没有被及时清理的定时器或回调函数、闭包
- 静态分配和对象池(?)