第十二章 变量作用域及内存

  JavaScript的变量与其它语言的变量有很大的区别。JavaScript变量是松散型(不强制类型)本质,决定了它只是在特定的时间用保存特定值的一个名字而已。由于不存在定义某个变量必须要保存何种数据类型值的规则,变量的值及其数据类型可以在脚本的生命周期内改变。

一、变量及作用域

  1、基本类型和引用类型
  ECMAScript变量可以包含两种不同的数据类型的值:基本类型值和引用类型值。基本类型值指的是那些保存在栈内存中的简单数据段,即这种值完全保存在内存中的一个位置。而引用类型值则是指那些保存在堆内存中的对象,意思是变量中保存的实际上只是一个指针,这个指针指向内存中的另外一个位置,该位置保存对象。

  将一个值赋给变量时,解析器必须确定这个值是基本类型值,还是引用类型值;基本类型值有:Undefined,Null,Boolean,Number,String。这些类型在内存中分别占有固定大小的空间,他们的值保存在栈空间,我们通过按值来访问的。

  如果赋值是引用类型的值,则必须在堆内存中为这个值分配空间。由于这种值的大小不固定,因此不能把它们保存在栈内存中。但内存地址大小固定的,因此可以将内存地址大小保存在栈内存中。这样当查询引用类型变量时,先从栈中读取内存地址,然后通过地址找到堆中的值。

      

  2、动态属性

  定义基本类型值和引用类型值的方法是相似的:创建一个变量并为该变量赋值。但是,当这个值保存在变量中以后,对不同类型的值可以执行的操作则大相径庭。

  var pesson=new Object();      //创建引用类型

  pesson.name="boke";        //新增一个属性

  alert(pesson.name);         //输出

  如果基本类型值添加属性:

  var pesson="boke";         //创建一个基本类型

  pesson.age=29;           //给基本类型添加属性

  alert(pesson.age);          //undefined

  3、复制变量值

  在变量复制方面,基本类型和引用类型也有所不同。基本类型复制的是值的本身,而引用类型赋值的是地址。

  var box="Lee";           //在栈内存中生成一个box "Lee";

  var box2=box;          //在栈内存再生成一个box2 "Lee";

    

  box2虽然是box的一个副本,但从图可以看出,它们是独立的。也就是说,两个变量分别操作互不影响。

  var box=new Object();      //创建一个引用类型

  box.name="Lee"; 

  var box2=box;

   

  在引用类型中,box2其实就是box,因为他们指向的是一个对象。如果这个对象name属性的值修改,则另外一个也会被修改。

  4、传递参数

  ECMAScript中所有函数的参数都是按值传递的。

  function addTen(num){

    num += 10;

    return num;

  }

  var count = 20;

  var result = addTen(count);

  alert(count);              //20 没有变化

  alert(result);              //30

  传递的参数是一个基本类型,而函数里面的num是一个局部变量,和外面的的count没有任何关系。

  下面一个引用类型的例子。

  function setName(obj){

    obj.name="bo";

  }

  var persson = new Object();

  setName(persson);

  alert(persson.name);      //bo

  如果按引用传递的话,那么函数里面的那个变量将会是全局变量,在外部也可以访问。

  5、检测类型

  要检测一个变量的类型,我们可以用过typeof运算符来判别:

  var name = "类型";

  alert(typeof name);        //string

  虽然typeof运算符在检查基本的类型的时候非常好用。但检测引用类型的时候,它就不是那么好用。通常,我们并不向知道它是不是对象,而想知道它是什么类型的对象。因为数组也是object,null也是object类型等等;

  这时我们要采用instanceof运算符来查看。

  var num = [1,2,3];

  alert(num instanceof Array);          //true 是否是数组

  var num2={};

  alert(num2 instanceof Object);         //true 是否是对象

  var num3=/g/;

  alert(num3 instanceof RegExp);        //true 是否是正则表达式

  var num4=new String("Lee");

  alert(num4 instanceof String);         //true 是否是字符串

  当instanceof 检测基本类型的值,会返回false;

  5、执行环境及作用域

  执行环境是javascript中最重要的一个概念。执行环境定义了变量或函数有权访问的其它数据,决定了他们各自的行为。

  全局执行环境是最外围的执行环境。在WEB浏览器中,全局执行环境被认为是window对象。因此所有全局变量和函数都作为window对象的属性和方法创建的。

  var num = 123;            //声明一个全局的变量

  function sum(){

    alert(num);            //全局变量可以在函数内部访问 

  }

  function sum2(){

    alert(window.num);        //全局变量即window属性

  }

  sum();                //调用函数

  window.sum2();           //全局函数即window函数

  当执行环境中的所有代码执行完毕后,该环境被销毁,保存在其中的所有变量和函数定义也随之销毁。如果在全局环境下,需要程序执行完毕,或则网页关闭才销毁。

  每个执行环境都有一个与之关联的变量对象,就好比全局的window可以调用变量和属性一样。局部的环境也是一个类似window的变量对象,环境中定义的所有变量和函数都保存在这个对象中。

  var num = 100;

  function setNum(){

    var num = 200;

    alert(num);

  } 

  setNum();      //200

  alert(num);     //100

  6、没有块级作用域

  块级作用域表示诸如if语句等有花括号封闭的代码块,所以,支持条件判断定义变量。

  if(true){

    var num = 100;

  } 

  在if语句中定义的变量num,在if语句执行完毕后被销毁。

  

内存问题

  JavaScript具有自动垃圾收集机制,也就是说,执行环境会负责管理代码执行过程中使用的内存。其他语言比如C和C++,必须手工跟踪内存使用情况,适时的释放,否则会造成很多问题。而JavaScript则不需要这样,它会自行管理内存分配及无用内存的回收。

  JavaScript最常用的垃圾收集方式是标记清除。垃圾收集器会在运行的时候给存储在内存中的变量加上标记。然后,它会去掉环境中正在使用变量的标记,而没有被去掉标记的变量将被视为准备删除的变量。最后,垃圾收集器完成内存清理工作,销毁那些带标记的值并回收他们所占用的内存空间。

  垃圾收集器是周期性运行的,这样会导致整个程序的性能问题。比如IE7以前的版本,它的垃圾收集器是根据内存分配量运行的,比如256个变量就开始运行垃圾收集器,这样,就不得不频繁地运行,从而降低的性能。

  一般来说,确保占用最少的内存可以让页面获得更好的性能。那么优化内存的最佳方案,就是一旦数据不再有用,那么将其设置为null来释放引用,这个做法叫做解除引用。这一做法适用于大多数全局变量和全局对象。

  var obj = {

    num = 100;

  };

  obj = null;          //解除对象引用,等待垃圾收集器回收 

  

原文地址:https://www.cnblogs.com/xchit/p/JavaScript_12.html