Javascript高级之变量、作用域与内存
变量、作用域与内存
原始值与引用值
-
理解要点
- ECMAScript中所有函数的参数都是按值传递的
- 函数外的值会被复制到函数内部的参数中
- 关于引用值(对象),也是按值传递的
- 理解
- 对象中保存的值是对象的key,也就是指针
- 函数中按值传递时,可以给指针赋值,影响全局变量
- 但是改变不了对象的key
- 可以用对象的动态属性来理解
- 理解
-
确定类型
- 如果变量是给定引用类型的实例,则instanceof操作符返回true
- 如果用instanceof检测原始值,则始终会返回false
let obj= null;
let obj2= {};
let fun= Function.prototype;
let fun2= new Function();
let arr= [];
let str= '';
let reg= new RegExp();
let date= new Date();
console.log(obj instanceof Object); // false
console.log(obj2 instanceof Object); // true
console.log(fun instanceof Function); // false
console.log(fun2 instanceof Function); // true
console.log(arr instanceof Array); // true
console.log(str instanceof String); // false
console.log(reg instanceof RegExp); // true
console.log(date instanceof Date); // true
执行上下文与作用域
-
执行上下文
- 每个上下文都有一个关联的变量对象
- 所有变量和函数都存在于这个对象上
- 这个对象就是上下文栈,在其所有代码执行完毕之后,就会被销毁
- var定义的全局变量和函数,会成为window对象的属性和方法
-
作用域链
- 上下文中的代码在执行的时候,会创建变量对象的一个作用域链
- 作用域链决定了各级上下文中的代码在访问变量和函数时的顺序
- 如果上下文是函数,则其活动对象用作变量对象
- 活动对象最初只有一个定义变量:arguments
- arguments是包含函数形参的一个对象
- 全局上下文没有这个变量
-
作用域链增强
- 在作用域链前端添加一个变量对象
- with语句
- 会向作用域链前端添加指定的对象
- try/catch语句中的catch块
- 会创建一个新的变量对象,这个变量对象会包含要抛出的错误对象的声明
- with语句
- 在作用域链前端添加一个变量对象
变量声明
-
使用var的函数作用域声明
- 变量会被自动添加到最接近的上下文
- 未声明的变量,会自动被添加到全局上下文
- var声明会被拿到函数或全局作用域的顶部,叫做提升
-
使用let的块级作用域声明
- 块级作用域由最近的一对包含花括号{}界定
- let在同一作用域内不能声明两次
- let非常适合在循环中声明迭代变量
-
使用const的常量声明
- const声明的变量必须同时初始化为某个值
- const声明只应用到顶级原语或者对象
- 使用
Object.freeze()
给对象赋值常量
垃圾回收
-
标记清理
- 先给当前不使用的值加上标记,再回来回收它们的内存
-
引用计数
- 对每个值都记录它被引用的次数
- 当引用次数为0时,垃圾回收程序会释放引用数为0的值的内存
-
循环引用
- 对象A有一个指针指向对象B,而对象B也引用了对象A
- 只要涉及COM对象,就无法避免循环引用的问题
内存管理
-
解除引用
- 如果数据不再必要,那么把它设置为null,从而释放其引用
-
通过const和let声明提升性能
- 块作用域比函数作用域更早让垃圾回收程序介入
-
隐藏类和删除操作
- 在构造函数中一次性声明所有的属性,因此实例共享隐藏类
- 把不想要的属性设置为null,可以保持隐藏类不变和继续共享
-
内存泄漏
- 没有使用关键字声明变量
- 定时器的回调通过闭包引用了外部变量
- 闭包造成的内存泄漏
-
静态分配和对象池
- 避免动态分配操作