变量, 作用域, 内存
原始值与引用值
原始值( primitive value ): 最简单的数据, Undefined, Null, Boolean, Number, String, Symbol. 保存原始值的变量是按值访问的, 也就是直接操作存储在变量中的实际值. ( by value )
引用值( reference value ): 由多个数据构成, 保存在内存中的对象. 但是JavaScript是不允许直接访问内存位置的. 在操作对象时, 实际上操作的是对改对象的引用而不是对象本身. ( by reference )
动态属性
- 原始值 -- 不可以添加属性
- 引用值 -- 可以添加属性
复制值
- 原始值 -- 值复制到新的变量
- 引用值 -- 指针复制到新的变量
(函数)传递参数
-
按值传递
- 按值传递参数时, 值会被复制到一个局部变量( 一个命名参数 - arguments的一个槽位中 )
- 这是JavaScript的传参行为
-
按引用传递
- 按引用传递参数时, 值在内存中的位置会被保存在一个局部变量中, 也就是说在函数内对入参的修改会反映到函数外部
这在JavaScript中是不存在的(不太理解原著中想表达的是什么东西在ECMAScript中不存在)
function setName (obj) {
obj.name = 'dyc';
obj = new Object();
obj.name = 'fxddf';
}
let person = new Object();
setName(person);
console.log(person); // {name: "dyc"}
确定类型
typeof
typeof "dyc" // string
typeof true // boolean
typeof 100 // number
typeof dyc // undefined
typeof null // object
typeof {name: 'dyc'} // object
typeof [1, 2, 3] // object
// typeof 不能区分不同的 object
instanceof
// 我们通常不关心一个值是不是对象,而是想知道它是什么类型的对象。为了解决这个问题,ECMAScript提供了instanceof操作符
result = variable instanceof constructor
console.log(person instanceof Object); // 变量person是Object吗?
console.log(colors instanceof Array); // 变量colors是Array吗?
console.log(pattern instanceof RegExp); // 变量pattern是RegExp吗?
执行上下文与作用域
执行上下文在JavaScript中很重要. 变量或函数的上下文决定了他们可以访问哪些数据, 以及他们的行为. 每个上下文都有一个关联的变量对象( variable Object ), 而这个上下文中定义的所有变量和函数都存在于这个对象上. 无法通过代码访问到变量对象, 但是后台处理数据会用到它.
执行上下文
-
全局上下文 -- 最外层上下文
-
浏览器中, 全局上下文 -- window对象
var定义的全局变量和函数都是 window 对象的属性和方法let和const的顶级声明不会定义在全局上下文中, 但在作用域链上解析效果是一样的.
-
-
函数上下文
-
eval调用内部存在第三种上下文
作用域
-
作用域链增强
-
try/catch的catch -
with[with] 由于 with 语句影响性能且难以调试其中的代码, 通常不推荐在产品代码中使用
[with] with 不能再严格模式下使用
-
变量声明
-
var- 变量自动被添加到最接近的上下文
- 如果变量未经声明就被初始化了, 会被自动添加到全局上下文
- 声明会被拿到上下文的顶部, 位于所有代码之前
-
let- 作用域是块级
- 同一作用域内不能声明两次
let在 js 运行时也会被提升, 但由于"暂时性死区", 不能再声明之前使用let变量
-
const-
同
let -
一经声明, 再其声明周期的任何时候都不能再重新赋值
-
对象的键不受限制
-
多使用
const[const] 由于
const声明暗示变量的值是单一类型且不可修改,JavaScript 运行时编译器可以将其所有实例都替换成实际的值,而不会通过查询表进行变量查找。谷歌的V8引擎就执行这种优化。
-
-
标识符查找
- 会一级一级作用域往上找, 使用找到的第一个
垃圾回收
-
标记清理 -- 最常用
-
引用计数 -- 没那么常用
-
性能
- 无论什么时候开始收集垃圾,都能让它尽快结束工作
-
内存管理
-
解除引用
-
如果数据不再必要, 设置
null释放其引用[注意] 解除对一个值的引用并不会自动导致相关内存被回收。解除引用的关键在于确保相关的值已经不在上下文里了,因此它在下次垃圾回收时会被回收。
-
-
通过const 和 let 声明提升性能
- 因为
const和let都以块(而非函数)为作用域,所以相比于使用var,使用这两个新关键字可能会更早地让垃圾回收程序介入,尽早回收应该回收的内存。
- 因为
-
隐藏类和删除操作
-
V8在将解释后的JavaScript代码编译为实际的机器码时会利用“隐藏类”
- 动态删除属性与动态添加属性导致的后果一样。最佳实践是把不想要的属性设置为
null。这样可以保持隐藏类不变和继续共享,同时也能达到删除引用值供垃圾回收程序回收的效果。
- 动态删除属性与动态添加属性导致的后果一样。最佳实践是把不想要的属性设置为
-
-
内存泄露
-
未使用声明符号
-
一直运行的定时器
-
闭包
let outer = function() { let name = 'Jake'; return function() { return name; }; }; /** * outer 执行完毕,按道理应该会销毁 name, * 但是 newFunction,即 outer 的内函数会一直占用 name,导致 name 不会被销毁。 * 注意:跟内部是不是匿名函数无关 * 注意:跟 return 的这个函数是否引用父函数的变量有关。 */ let newFunction = outer(); newFunction = null // 清理闭包的内存泄漏
-
-
静态分配与对象池
-
减少浏览器执行垃圾回收的次数
[静态分配] 静态分配是优化的一种极端形式。如果你的应用程序被垃圾回收严重地拖了后腿,可以利用它提升性能。但这种情况并不多见。大多数情况下,这都属于过早优化,因此不用考虑。
-
-