《JavaScript高级程序设计第四章--变量、作用域、和内存问题》

4.1基本类型和引用类型

变量可能包含两种不同的数据类型的值:基本类型(undefined、Null、Boolean、 Number和String都是按值传递的,因为可以操作保存在变量中的实际值)和引用类型(指保存在内存中的对象,与其他编程语言不同,JavaScript不允许直接访问内存中的位置,也就是说不能直接操作对象的内存空间,在操作对象时,实际在操作对象的引用而不是实际的对象,因 此,引用类型的值是按引用的);以下是它们的不同之处知识点

--4.1.1动态属性

将一个值赋给变量时,解析器必须确定这个值是基本类型值还是引用类型值。

定义这两种类型值的方式类似,但可执行的操作则大相径庭;比如对于引用类型的值可以为其添加属性和方法,也可以改变和删除他们,如果对象不被销毁或者这个属性不被删除,这个属性就一直存在。如果我们给基本类型添加属性,如下

var name="beHeadmaster";
name.age="27";
alert(name.age);//输出undefined;虽这种添加属性的做法不会出错;但会在下一行的时候立即删除;

--4.1.2复制变量

1)复制基本类型:完全独立,参与操作时不会相互影响;(值都保存在栈中);

2)复制引用类型:将存储在变量对象中的值复制一份给新变量分配的空间,不同的是,这个副本值是一个指针,指向存储在堆中的一个对象,这两个变量都是引用同一个对象,改变其中一个变量,就会影响另一个变量诶;

 

var obj1 = new Object();
var obj2 = obj1;
obj1.name="headmaster";
alert(obj2.name);//输出就是“headmaster”;

--4.1.3传递参数

所有函数的参数都是按值传递的(基本类型是值,引用类型是指针地址)

1)在向参数传递基本类型的值时,被传递的值会被赋值给一个局部变量(即命名参数,或者用ECMAScript的概念来说,就是arguments对象中的一个元素;)

 1 function addTen(num){
 2        num+=10;
 3         return num;
 4 }
 5 var count = 20;
 6 var result =addTen(count);
 7 alert(count);//20,没有变化
 8 alert(result);//30
 9 /*
10     *解释说明
11     *变量count被赋值给函数的局部变量num,在函数内部num值改变,也不会影响函数外部count变量,如果是按引用传递的话就应该是count=30,显然不是;
12     */

 

2)在向参数传递引用类型的值时,会把这个值在内存中的地址复制给一个局部变量,因此这个局部变量的变化会反应在函数的外部。如下例子

/*
    使用数值等基本类型值来说明按值传递参数比较简单,如果适用对象,
*/
function setName(obj){
  obj.name = "Nicholas";
  obj = new Object();
  obj.name = "Greg"; 
}
var person = new Object();
setName(person);
alert(person.name);//"Nicholas"
/*
  如果person是按引用传递的,那么person就会自动被修改为指向其name属性值"Greg"的新对象,但是却显示"Nicholas".这就说明即使函数在内部修改了参数的值,但原始的引用仍然保持不变。
 实际上,当函数内部重写obj时,这个变量引用的就是一个局部对象了,会在函数执行完毕后立即被销毁; */

--4.1.4检测类型

  1)typeof操作符是检测一个变量是不是基本数据类型的最佳工具(是字符串?数值?布尔值?还是undefined?);如果变量的值是一个对象或null,则使用typeof操作符会返回"object";(注意:typeof检测函数时会返回"function",在ie和Firefox中对正则表达式应用会返回"object")

  2)所以在检测引用类型的值时,这个操作符的用处不大,毕竟我想知道他是什么类型的对象。因此有instanceof操作符,返回布尔值;如果,instanceof检测基本类型的值,则该操作符始终会返回false,因为基本类型不是对象。

4.2执行环境及作用域

  执行环境定义了变量或函数有权访问的其他数据,决定了他们各自的行为。

  每个执行环境都有一个与之关联的变量对象(VO),环境中定义的所有变量和函数都保存在这个对象中。虽然我们编写的代码无法访问这个对象,但解析器在处理数据时会在后台使用它。(不必深入)

  首先来认识执行环境

  1)全局执行环境是最外围的一个执行环境,根据ECMAScript实现所在宿主环境不同,执行环境的对象也不一样。在web浏览器中,全局执行环境被认为是window对象(第七章有讨论到),因此,所有全局变量和函数都是作为window对象的属性和方法创建的。执行环境中代码执行完后,该环境被销毁,其中保存的所有变量和函数定义也随之销毁(全局环境直到应用程序退出,如关闭网页或浏览器)//这里有待补充,好像什么闭包,还可以保存变量,并没有随之销毁,外部可用

  2)每个函数都有自己的执行环境(局部环境)。当执行流进入一个函数时,函数的环境就会被推入一个环境栈中。而在函数执行之后,栈将其环境弹出,并把控制权返回给之前的而执行环境;

  

  当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain用途是保证对执行环境有全访问的所有变量和函数的有序访问).作用域链的前端,始终都是当前执行的代码在环境的变量对象。如果这个环境是函数,则将其活动对象作为变量对象。活动对象在最开始时只包含一个变量,即arguments对象(这个对象在全局环境中是不存在的)。作用域链中的下一个变量对象来自包含(外部)环境,一直这样延续到全局环境(始终是作用域链的最后一个对象;)更详细的博文地址http://web.jobbole.com/84044/

  标识符解析是沿着作用域链一级一级地搜索标识符的过程,这个过程始终从作用域链的最前端开始,然后逐级向后回溯,直到找到标识符为止(如果找不到通常会导致错误发生哦)。

  来个例子

 

 1 var color = "blue";
 2 function changeColor(){
 3   var anotherColor = "red";
 4   function swapColors(){
 5     var tempColor = anotherColor;
 6     anotherColor = color;
 7     color = tempColor;
 8   // 这里可以访问color、anotherColor 和tempColor
 9     }
10   // 这里可以访问color 和anotherColor,但不能访问tempColor
11   swapColors();
12 }
13 // 这里只能访问color

 

 

 

   /*代码解析

  以上代码共涉及3个执行环境:全局环境、changColor()的局部环境和swapColors()的局部环境。

  全局环境中有一个变量color和一个函数changeColor();

  changeColor()的局部环境中有一个anotherColor()、和swapColors()函数;

   swapColors()的局部环境中有一个变量tempColor,只能在这个环境中访问到。其他环境无权访问。因为其他两个环境是他的父执行环境,所以swapColors()内部则可以访问其他两个环境中的所有变量(如图4-3);*/

  内部环境可以通过作用域链访问所有的外部环境,但外部环境不能访问内部环境中的任何变量和函数。这些环境之间的联系是线性的、有次序的。每个环境都可以向上搜索作用域链,以查询变量和函数名;注意不能通过向下搜索;一般而言,会先在自己的变量对象中搜索变量和函数名,如果搜索不到则在搜索上一级作用域链

  4.2.1延长作用域链的语句

  1)try-catch语句的catch块; 2)with语句。(不必深入)

  4.2.2 没有块级作用域

  不想其他语言那样,JavaScript并没有块级作用域。如

if(true){
    var color = "blue";
}
alert(color);//"blue"
//这里if语句块中定义了变量color。要知道在C、java中,会在if语句执行完毕后被销毁;而对于JavaScript,if语句中的变量声明会将变量添加到当前的执行环境中(这里是全局环境)

 

 再比如for语句块中要注意的差异

for (var i =0; i < 10; i++){
    doSomething(i);
}
alert(i);  //输出10

 

    4.2.2.1声明变量

  使用var声明的变量会自动被添加到最接近的环境中;如果初始化变量时没有用var声明,该变量会自动被添加到全局变量(注:当然和var定义的全局变量还是有区别的,待补充。)。所以在编写js代码过程中,不var声明而直接初始化变量,可能会导致意外;在严格模式下,初始化未经声明的变量会导致错误。

  4.2.2.2查询标识符

  就是从当前环境中的作用域链开始搜索标识符,找到就停止,变量就绪,没找到就继续向上级查找看看有没有,如果一直这样向上都没有,那意味着该变量尚未声明。(注意:在这个搜索过程中,如果存在一个局部变量的定义,则搜索会自动停止,不在进入另一个变量对象。就是说,局部环境中存在着同名标识符,就不会使用位于父环境中的标识符。)

var color = "blue";
function getColor(){
    var color = "red";
    return color;
}
alert(getColor());  //"red",这方便JavaScript引擎在优化标识符查询方面做得不错,因为访问局部变量要比访问全局变量要更快,不用向上搜索作用域链;

 

4.3垃圾回收机制

  JavaScript是一门具有自动垃圾收集机制的编程语言,开发人员不必关心内存分配和回收问题;

  

posted @ 2016-02-24 10:38  短短几年当校长  Views(141)  Comments(0)    收藏  举报