ECMAscript中的变量是松散类型的,即它在不同的时期可以有不同类型的值,这也是ECMAscript最强大的的特性之一.
基本类型和引用类型的值
javascript的值类型有两种:基本类型
引用类型
。基本类型的值是指的简单的数据段,基本类型有五种,依次是:Undefined
Boolean
String
Number
Null
,而引用类型的值指的是由多个值构成的对象.
基本类型值和引用类型值的区别
保存方式不同
基本类型值的变量是保存确实的值.而引用类型值的变量保存的是一个指针,这个指针指向内存中的对象.
引用类型的值可以动态添加属性和方法.
复制形式不同
当基本类型值的变量被复制给另一个变量时,内存中会新开辟一个存储空间来存储这个新变量的值,新变量的值和被复制的变量的值保存在两个不同的存储空间中,它们相互独立,互不影响.
当引用类型值的变量被复制给另一个变量时,内存中不会开辟新的空间,新变量保存的也是一个指向内存中对象的指针,新变量和旧变量指向的是同一个存储区域,因此改变其中一个变量的值,另一个变量也会随之改变.
var name = 'yuhualingfeng';var anotherName = name;alert(anotherName); // yuhualingfenganoherName = 'Jake';alert(anotherName); // Jakealert(name); //yuhualingfeng
如上所示,改变了anotherName
的值并没影响到name
变量的值.
var obj = new Object();obj.name = 'yuhualingfeng';var anotherObj = obj;alert(anotherObj.name); // yuhualingfenganotherObj.name = 'Jake';alert(anotherObj.name); // Jakealert(obj.name); Jake
改变anotherObj
对象的name
属性值后,obj
对象的name
属性值也随之改变,这一点可以判断出二者指向的是同一个存储区域.
执行环境(作用域)
执行环境
是Javascript中最为重要的一个概念.执行环境定义了变量或函数有权访问的其他数据,定义了它们各自的行为.每个执行环境都有一个与之关联的变量对象
全局执行环境
是最外层的执行环境,全局执行环境中有一个window全局对象,全局环境中的所有变量都可以通过window对象访问.
除全局执行环境外,每个函数也都有一个自己的执行环境.当执行某个函数时,会产生一个由变量对象组成的作用域链
,作用域链的存在是为了让解析器能够有序的找寻标识符,假如在函数中使用某个变量时,首先解析器会在函数自身的变量对象中找寻这个变量的标识符,假如没找到,会继续查找上一个变量对象,直到找到这个标识符为止.查找标识符时,
var age = 30;function changeAge(){age = 40;}changeAge();alert(age); // 40
changeAge
的作用域链中有两个变量对象,自身的变量对象和全局环境的变量对象,当执行changeAge
函数时,调用了age
变量,解析器先在changeAge
变量对象中查找此变量,没有找到,然后继续查找上一个变量对象全局变量对象,找到了age
变量.
有一点要注意的是,javascript中的作用域和其他编程语言不同,它没有块级作用域,看下面的例子:
function test(){ for(var i = 0,len =10;i < len;i++){ doSomething(i); console.log(i); // 10 }}test();
在for语句中定义的i
变量,在for
语句外可以被访问到.javascript中定义的变量会把它添加到最近执行环境的变量对象中.这里会把i变量添加到test
函数的变量对象中.
垃圾收集
Javascript具有自动垃圾收集机制,执行环境会负责管理代码执行过程中使用的内存.
垃圾回收机制的原理:垃圾收集器找出那些不再继续使用的变量,然后释放其占用的内存,垃圾收集器会按固定的事件周期性的执行内存回收.
用于标记无用变量策略因实现而异,具体到浏览器中的插件,通常有以下两种策略:标记清楚
引用计数
标记清除
标记清除是浏览器中最常用的一种垃圾收集方式.当变量进入环境时(例如用var 声明一个变量),就将这个变量标记为进入环境
,当变量离开环境时,就将这个变量标记为离开环境
,当然,做这种标记的方式可以有多种,比如可以通过翻转某个特殊的位来记录变量合适进入环境,或者使用一个"进入环境的"变量列表及一个"离开环境的"变量列表来跟踪哪个变量发生了变化.
垃圾回收器会给所有存储在内存中的变量都加上标记(进入环境),当变量在执行环境中无法被访问到时,就会加上另一种标记(离开环境),待下一次垃圾回收器进行回收时将回收其占用的内存.
引用计数
另一种不太常见的回收方式是引用计数.引用计数就是计算出变量被引用的次数,当一个变量被赋值为一个引用类型的值时,引用类型值的引用次数就加1,当这个变量指向另一个引用类型的值时,引用类型值的引用次数就减1.当垃圾回收器进行检测时,发现引用类型的值被引用的次数等于0时就会回收其占用的空间.
这种垃圾回收机制会在循环引用
中导致严重的内存泄露,看下面例子: function Test(){ var obj1 = new Object(); var obj2 = new Object(); obj1.property = obj2; obj2.property = obj1;}
当Test
函数执行完成后,通过引用计数的机制进行垃圾回收时,会发现obj1指向的引用类型值的引用计数为1(被obj2.property引用),obj2指向的引用类型值的引用计数为1(被obj1.property引用),因此这两个引用类型的值就不会被回收,假如多次执行此函数就会造成严重的内存泄露.
obj1.property = null;obj2.property = null;
IE浏览器中IE9以前的版本的BOM和DOM中的对象采用的就是引用计数的回收机制,因此我们在使用时需格外注意.
还有一点需要注意的是:全局环境中的变量只会在应用程序结束(比如关闭浏览器)时才会离开执行环境,所以我们需要尽可能的手动释放无用的变量占用的内存.