🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] # 变量的值、作用域和内存问题 JavaScript变量松散类型的本质,决定了它只是在特定时间用于保存特定值的一个名字。由于不存在定义某个变量必须保存何种数据类型值的规则,变量的值及其数据类型可以在脚本的生命周内改变。 ## 4.1 基本类型和引用类型的值 **ECMAScript中的所有参数传递的都是值,不可能通过引用传递参数。** ECMAScript变量可能包含两种不同数据类型的值:基本类型值和引用类型之。**基本类型值**源自于基本数据类型,**引用类型值**则指那些可能由多个值构成的对象。 两种数据类型的值的**特点**: * 基本类型值在内存中占据固定大小的空间,因此被保存在**栈内存**中; * 从一个变量向另一个变量**复制***基本类型*的值,会创建这个值的一个**副本**; * 引用类型的值是**对象**,保存在**堆内存**中; * 包含引用类型的变量实际上包含的并不是对象本身,而是一个指向该对象的**指针**; * 从一个变量向另一个变量复制*引用类型*的值,复制的其实是指针,因此两个变量最终都**指向同一个对象**; 确定一个值是哪个基本类型可以用`typeof`操作符,而确定一个值是哪种引用类型可以使用`instanceof`操作符,如: ~~~ alert(person instanceof Object); //变量person是Object吗? alert(colors instanceof Array); //变量colors是Array吗? ~~~ ## 4.2 执行环境及作用域 所有变量(包括基本类型和引用类型)都存在于一个**执行环境**(也成为**作用域**)当中,这个执行环境决定了变量的生命周期,以及哪一部分代码可以访问其中的变量。 某个执行环境中的所有代码执行完毕后,该环境被销毁,保存在其中的所有变量和函数定义也随之销毁(全局执行环境直到应用程序退出--例如关闭网页或浏览器--时才会被销毁)。 在Web浏览器中,全局执行环境被认为是window对象,因此所有全局变量和函数都是作为**window对象**的属性和方法创造的。 **执行环境总结:** * 执行环境有**全局**执行环境(也称全局环境)和**函数**执行环境之分; * 每次进入一个新执行环境,都会创建一个用于**搜索变量和函数**的**作用域链**; * 函数的局部环境不仅有权访问函数作用域中的变量,而且有权访问其包含(父)环境,乃至全局环境; * 全局环境只能访问在全局环境中定义的变量和函数,而**不能直接访问局部**环境中的任何数据; * 变量的执行环境有助于确定应该**何时释放内存**。 **延长作用域链** ~~~ try-catch语句的catch块 with语句 ~~~ 这两个语句都会在**作用域链的前端**添加一个变量对象。对with语句来说,会将指定对象添加到作用域链中。对catch语句来说,会创建一个新的变量对象,其中包含的是被抛出的错误对象的声明。 **没有块作用域** JavaScript中没有块级作用域,因此if语句中的变量声明会添加到当前的执行环境(下例为全局环境)中。 在使用for语句时尤其要牢记这一差异,例如: ~~~ for (var i=0; i <10; i++) { doSomething(i) } alert(i); // 10 ~~~ 由for语句创建的变量i即使在for循环执行结束后,依旧会存在于循环外部的执行环境中。 ## 4.3 垃圾收集 JavaScript是一门具有自动垃圾收集机制的编程语言,开发人员不必关心内存分配和回收问题。 * 离开作用域的值将呗自动标记为可以回收,因此将在垃圾收集期间被删除。 * "标记清除"是目前主流的垃圾收集算法,算法思想是给当前不使用的值加上标记,然后再回收其内存。 * "引用计数"的算法思想是跟踪记录所有值被引用的次数。(淘汰)当代码中存在循环引用现象时,会导致问题。 * 解除变量的引用不仅有助于消除循环引用现象,而且对垃圾收集也有好处。为了确保有效地回收内存,应该及时解除不再使用的全局对象、全局对象属性以及循环引用变量的引用。