变量、作用域和内存问题
1 变量
1.1 基本类型值(简单的数据段)
5种基本类型,按值访问
不能添加属性(即便不报错),该属性只是临时,并不能访问
1.2 引用类型值
保存在内存中的对象
操作对象的引用
EMCAScript中,字符串是基本类型值,而很多语言中大多是引用类型
可以添加属性,并且是动态的,以便将来使用
注意:JavaScript不允许直接操作对象的内存空间
1.3 复制变量值
基本类型值复制的过程:在变量对象上创建一个新值,然后把该值复制 到为新变量分配的位置上(相互独立)
引用类型值复制的过程:讲储存在变量对象的值复制一份给新变量,实际上是将引用(地址)复制给变量,两个变量将引用同一个对象
1.4 传递参数
ECMAScript所有函数的参数都是按值传递
基本类型与引用类型的值都以其方法来传递(注意:引用类型值是以引用传递的)
1.5 检测类型
typeof操作符
确定变量是否为字符串、数值、布尔值还是 undefined
遇到对象或null会返回“object”
检测函数时候会返回function
ECMA-262规定任何在内部实现[[Call]]方法 的对象都应该在应用 typeof 操作符时返回"function"
正则表达式也返回function(在 IE和 Firefox中,对正则表达式应用 typeof 会返回"object"。 )
instanceof操作符(只用来检测引用类型的变量,不适用于基本类型,基本类型检测会返回false)
2 执行环境(execution context)及作用域
每个执行环境都有一个 与之关联的变量对象(variable object),环境中定义的所有变量和函数都保存在这个对象中。
全局执行环境是最外围的一个执行环境(Web中被认为是window对象)
每个函数都有自己的执行环境。当执行流进入一个函数时,函数的环境就会被推入一个环境栈中。 而在函数执行之后,栈将其环境弹出,把控制权返回给之前的执行环境。
在环境中执行时,会创建变量对象的一个作用域链(作用链用途:保证对执行环境有权访问的所有变量和函数的有序访问)
函数环境:将活动对象(activation object)作为变量对象
内部环境可以通过作用域链访问所有的外部环境,但 外部环境不能访问内部环境中的任何变量和函数(联系是线性、有次序)
函数参数也被当作变量来对待,因此其访问规则与执行环境中的其他变量相同。
2.1 延长作用域链
执行流进入try-catch语句和catch块;with语句会得到延长。
catch语句会创建一个新的变量对象,其中包含的是被抛出的错误对象的声明。
with语句会将指定的对象添加到作用域链中。
2.2 没有块级作用域
在其他类 C 的语言中,由花括号封闭的代码块都有自己的作用域。
ECMAScript是它们自己的执行环境。
在 JavaScript中,if 语句中的变量声明会将变量添加到当前的执行环境(在这里是 全局环境)中(与C有差异)(尤其是for语句中)
注意var的使用(局部变量和全局变量)。
在严格模式下,初始化未经声明的变量会导致错误。
查询标识符:从局部到全局进行搜寻标识符,若都找不到则意味着该变量尚未声明;访问局部变量要比访问全局变量更快,因为不用向上搜索作用域链。
2.3 垃圾收集
JavaScript 具有自动垃圾收集机制
执行环境会负责管理代码执行过程中使用的内存
原理:找出那些不再继续使用的变 量,然后释放其占用的内存。
标记清除mark-and-sweep(一种垃圾收集方式)
垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记(“进入环境”和“离开环境”)
引用计数
跟踪记录每个值被引用的次数
循环引用指的是对象 A中包含一个指向对象B的指针,而对象B中也包含一个指向对象 A的引用
管理内存
分配给 Web 浏览器的可用内存数量通常要比分配给桌面应用程序的少(目的:对安全方面的考虑;防止运行 JavaScript 的网页耗尽全部系统内存而导致系统崩溃)
解除引用(dereferencing):将其值设置为 null 来释放其引用;局部变量会在它们离开执行环境时自动被解除引用;解除引用的作用是让值脱离执行环境,以便垃圾收集器下次运行时将其回收。