变量、作用域和内存问题
变量:JavaScript 变量松散类型的本质,决定了它只是在特定时间用于保存特定值的一个名字而已。
基本类型和引用类型
ECMAScript 变量的两种数据类型:
- 基本类型;
- 引用类型;
数据类型 | 基本类型 | 引用类型 |
---|---|---|
含义 | 简单的数据段,5种基本数据类型:Undefined、Null、Boolean、Number、String。 | 可能由多个值构成的对象。 |
访问方式 | 按值访问,可以操作保存在变量中的实际的值。 | 按引用访问,不能直接操作对象的内存空间。 |
保存区域 | 栈内存 | 堆内存 |
复制 | 开辟新的内存空间 | 两个指针引用同一个对象 |
动态的属性
- 引用类型:可以为其添加属性和方法,也可以改变和删除其属性和方法。
- 基本类型:不能给基本类型的值添加属性。
复制变量值
从一个变量向另一个变量复制基本类型的值,会在变量对象上创建一个新值,然后把该值复制到为新变量分配的位置上。
从一个变量向另一个变量复制引用类型的值,同样也会将存储在变量对象中的值复制一份放到为新变量分配的空间中。不同的是,这个值的副本实际上是一个指针,而这个指针指向存储在堆中的一个对象。复制操作结束后,两个变量实际上将引用同一个对象。因此,改变其中一个变量,就会影响另一个变量。
- 基本类型:会开辟新的内存,两个值互不影响。
- 引用类型:指针,指向存储在堆中的同一个对象。
传递参数
-
ECMAScript 中所有函数的参数都是按值传递的。
-
在向参数传递基本类型的值时,被传递的值会被复制给一个局部变量(即命名参数,或者用ECMAScript 的概念来说,就是 arguments 对象中的一个元素)。
-
在向参数传递引用类型的值时,会把这个值在内存中的地址复制给一个局部变量,因此这个局部变量的变化会反映在函数的外部。
// 基本类型传递参数 function addTen(num){ num += 10; return num; } var count = 20; var result = addTen(count); console.log(count); // 20,没有变化。 console.log(result); // 30
检测类型
-
typeof
操作符,检测一个值是哪种基本数据类型; -
instancehof
,引用类型-true,基本类型-false。
💡💡💡
typeof 操作符是确定一个变量是字符串、数值、布尔值,还是 undefined 的最佳工具。
执行环境
- 全局执行环境;
- 函数执行环境;
- 全局环境只能访问在全局环境中定义的变量和函数,而不能直接访问局部环境中的任何数据;
- 内部环境可以通过作用域链访问所有的外部环境,但外部环境不能访问内部环境中的任何变量和函数。这些环境之间的联系是线性、有次序的。每个环境都可以向上搜索作用域链,以查询变量和函数名;但任何环境都不能通过向下搜索作用域链而进入另一个执行环境。
延长作用域链
在作用域链的前端临时增加一个变量对象,该变量对象会在代码执行后被移除。
当执行流进入下列任何一个语句时,作用域链就会得到加长:
- try-catch 语句的 catch 块;
- with 语句。
没有块级作用域 & 作用域链
在 JavaScript 中,if 语句中的变量声明会将变量添加到当前的执行环境中。
if(true) {
var color = "blue"; // 变量会被添加到当前执行环境。
} // if语句结束后,变量不会被销毁。
// if{} 外部依然可以访问到内部声明的变量
alert(color); // "blue"
垃圾收集
JavaScript 具有自动垃圾收集机制,也就是说,执行环境会负责管理代码执行过程中使用的内存。
原理:找出不再继续使用的变量,释放其占用的内存。
- 标记清除:进入环境-离开环境;给当前不使用的值加上标记,然后再回收其内存。
- 引用记数;循环引用问题。
网友评论