1

ECMAscript中的变量是松散类型的,即它在不同的时期可以有不同类型的值,这也是ECMAscript最强大的的特性之一.

基本类型和引用类型的值

javascript的值类型有两种:基本类型 引用类型。基本类型的值是指的简单的数据段,基本类型有五种,依次是:Undefined Boolean String Number Null,而引用类型的值指的是由多个值构成的对象.

基本类型值和引用类型值的区别

保存方式不同

基本类型值的变量是保存确实的值.而引用类型值的变量保存的是一个指针,这个指针指向内存中的对象.

引用类型的值可以动态添加属性和方法.

复制形式不同

当基本类型值的变量被复制给另一个变量时,内存中会新开辟一个存储空间来存储这个新变量的值,新变量的值和被复制的变量的值保存在两个不同的存储空间中,它们相互独立,互不影响.

当引用类型值的变量被复制给另一个变量时,内存中不会开辟新的空间,新变量保存的也是一个指向内存中对象的指针,新变量和旧变量指向的是同一个存储区域,因此改变其中一个变量的值,另一个变量也会随之改变.


var name = 'yuhualingfeng';
var anotherName = name;
alert(anotherName);  // yuhualingfeng

anoherName = 'Jake';
alert(anotherName); // Jake
alert(name); //yuhualingfeng 

如上所示,改变了anotherName的值并没影响到name变量的值.

var obj = new Object();
obj.name = 'yuhualingfeng';

var anotherObj = obj;

alert(anotherObj.name); // yuhualingfeng

anotherObj.name = 'Jake';

alert(anotherObj.name); // Jake
alert(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中的对象采用的就是引用计数的回收机制,因此我们在使用时需格外注意.
还有一点需要注意的是:全局环境中的变量只会在应用程序结束(比如关闭浏览器)时才会离开执行环境,所以我们需要尽可能的手动释放无用的变量占用的内存.


yuhualingfeng
1.7k 声望49 粉丝

前端极致追求者