Spiga

Javascript真谛4——代码的时空

2008-08-15 21:10:57

  任何程序都会在一个原始的环境中开始运行,这个原始的环境就被称为全局环境。全局环境中包含了一些预定义的元素,这些元素对于我们的程序来说是自然存在的,它们本来就在那儿了,我们可以拿来使用即可。

  在javascript里的全局环境就是一个对象,这个对象是就是我们熟知的window对象。对于全局的javascript语句来说,window对象就相当于当前作用域。我们写下:var myName = "xiaosuo";

就是定义了window作用域的一个变量,而我们写下:myName = "xiaosuo";

就是定义了window对象的一个属性。

  这里,“window作用域的一个变量”和“window对象的一个属性”几乎等价。对于全局的javascript语句来说,加不加var都无所谓,不过对于一个函数内的语句,有没有var就要小心了。

var yourName = "horace";
myName = "xiaosuo";
​
alert(myName + " and " + yourName);        //输出xiaosuo and horace
​
ChangeNames();        //调用改名函数
​
function ChangeNames()
{
    alert("Your old name is " + yourName);        //输出Your old name is undefined
    alert("Ny old name is " + myName);            //输出My old name is xiaosuo
    var yourName = "anlian";
    myName = "liuzi";
​
    alert(myName + " and " + yourName);        //输出liuzi and anlian
};
​
alert(myName + " and " + yourName);        //输出liuzi and horace    

  显然用var修饰的yourName标识符在函数内外是两个东西,值得注意的是,看ChangeNames函数中的第一句,我们本是希望输出最外面哪个yourName的值,但此时输出的却是undefined!而第二句输出myName又是我们期望的值。原因是var定义的是作用域上的一个变量,而没有var的标识符却可能是全局对象的一个属性。

  javascript解释执行代码时,当遇到对函数名或变量名的使用时,javascript执行引擎会首先在当前作用域查找函数或变量,如果没有就到上层作用域查找,依次类推。因此前面的语句引用后面语句定义的var变量时,该变量其实已经存在,只是初始值为undefined。

  javascript提供的调用上下文信息有几个:一个是函数本身,一个是函数的celler属性,另外还有this关键字和arguments隐含对象。

  当代码运行在函数体内的作用域时空时,函数本身的标识符是可以用的。比如下面的代码就可以输出函数只是的定义:

function aFunc()
{
    alert(aFunc.toString());    //函数体内可以使用自身的标识符
}    
aFunc();    //调用函数

 更进一步,函数自身有一个caller属性,其表示调用当前函数的上层函数。这就提供了一个可以追溯函数调用来源的线索,特别对于调试javascript来说是不可多得的宝贝。下面的代码可以帮助我们理解caller属性的含义:

function WhoCallMe()
{
    alert("My caller is " + WhoCallMe.caller);        //输出自己的caller
};
​
function CallerA(){ WhoCallMe(); };
function CallerB(){ WhoCallMe(); };
​
alert(WhoCallMe.caller);    //输出null;
​
WhoCallMe();    //输出My caller is null
CallerA();        //输出My caller is function CallerA(){ WhoCallMe(); };
CallerB();        //输出My caller is function CallerB(){ WhoCallMe(); };

  如果函数的caller属性是Null,表示函数没有被调用或者是被全局代码调用。函数的caller属性值实际上是动态变化的。函数的初始caller值都是null,当调用一个函数时,如果代码已经运行在某个函数体内,javascript执行引擎会将被调用函数的caller属性设为当前函数。在退出被调用函数作用域时,被调用函数的caller属性又会恢复为null值。