Javascript高级程序设计第四章 | ch4 | 阅读笔记
变量、作用域与内存
原始值与引用值
什么是字面量形式?
let obj = {
key1: val1,
key2: val2,
foo () {
}
}
这就是字面量形式,手动声明一个对象的属性和方法,而不是通过构造函数。这样声明简单,但不能复用。 引用
-> 原始类型初始化只能使用字面量形式
复制值
原始值和引用值在变量复制时也有差别
1. 原始值的复制是拷贝
2. 引用值的复制实际上是指针的拷贝
传递参数
ECMAScript中参数传递只有按值传递,它的参数就是局部变量
- 如果是原始值则拷贝
- 如果是引用值则拷贝指针
-> 这就导致在局部作用域中修改obj的属性值,也会影响到外部。因为拷贝的引用是指向堆内存的
确定类型
- typeof:适用于判断变量是否为原始类型。特别是变量是否为字符串、数值、布尔值或undefined。如果是null,那么typeof就会返回object。typeof对于所有对象都返回object
- instanceof:如果想知道一个变量是什么类型的对象,可以用instanceof方法
执行上下文与作用域
变量和对象的上下文决定了它们能够访问哪些数据,以及它们的行为。每个上下文都有一个关联的变量对象,而这个上下文中定义的所有变量和函数都存于这个对象上。
全局上下文是最外层的上下文(window,global)
使用let和const的顶级声明不会定义在全局上下文中
当函数执行时,函数的上下文会被推到上下文栈顶,等函数执行结束,就会从上下文栈中弹出。
上下文中的代码在执行的过程中会创建变量对象的一个作用域链。这个作用域链决定了各级上下文中的代码在访问变量和函数时的顺序。
function layer_1() {
let color = 'red';
function layer2() {
console.log(color);
}
layer2();
}
layer_1(); // red
//-------------------------------------------------------------------//
let color = 'blue';
function layer2() {
console.log(color);
}
function layer_1() {
let color = 'red';
layer2();
}
layer_1(); // blue
// 也就是说函数执行的时候的上下文是与函数定义位置的上下文相关的。
如果这里把layer2的定义放到外面就会报错
函数参数被认为是当前上下文中的变量,因此也跟上下文中的其他变量遵循相同的访问规则
作用域链增强
- try/catch语句的catch块
- with语句
变量声明
- 使用var声明的变量会被自动添加到最接近的上下文,如果未经定义就被初始化了会被添加到全局上下文。
- var声明会被拿到函数或者作用域的顶端,在作用域所有代码之前。这个现象叫做:“提升”。提升让同一作用域中的代码不必考虑变量是否已经声明就可以使用。
console.log(age); // undifined
var age = 1;
- 使用let块级作用域声明。块级作用域由最近的一对包含花括号* { } * 界定。
- let与var的不同之处:
- 重复的var声明会被忽略,不会报错
- 重复的let声明会报错
- 使用var声明的循环迭代变量会泄露到循环外部
- let声明的变量会有暂时性死区,即在声明前调用会报错
- 使用const声明的变量必须同时赋值,且在其生命周期中不能再重新赋值。其余与let变量相同。
- const保存的对象虽然引用不能改变,但是属性不受限制。
- 如果希望整个对象都不能修改,可以使用Object.freeze()
- 使用标识符查找
垃圾回收
垃圾回收的基本思路:确定哪个变量不再使用之后,释放它占用的内存。这个过程是周期性的,垃圾回收程序每隔一定时间就会自动运行。
标记未使用的变量:
- 标记清理
- 引用计数
标记清理
引用计数
性能
内存管理
使用let和const会提高垃圾回收的性能