1. javascript中的变量
在javascript中使用一个变量应该声明。
变量是使用var关键字来声明的。
var i,sum;
可以将初始化和声明写在一起,如果声明之后没有初始化,那么它的值就是undefined。
var str = "niaho";
var i = 0;
var test;
document.writeln(test);
虽然javascript中全局变量可以不声明,直接赋值,但是不建议这么做,会造成很多bug。
nihao="hehe";
document.writeln(nihao);
2. 变量的作用域
一个变量的作用域是程序源代码中定义这个变量的区域。在javascript中全局变量拥有全局的作用域,在javascript中的任何地方都可以访问。然而在函数内部定义的变量只在函数体内有定义。函数参数也是局部变量,在函数体重有定义。
在函数体中,局部变量优先级高于全局变量。
var scope = "global";
function checkscope() {
var scope = "local";
return scope;
}
document.writeln(checkscope());//"local"
在函数体中定义局部变量必须使用var声明,否则会变成对同名全局变量的操作。
scope = "global"; //声明全局变量scope
function checkscope2() {
scope = "local"; //操作全局变量scope
myscope = "local"; //声明新的全局变量myscope
return [scope, myscope];
}
checkscope2();
scope; //"local"
myscope; //"local”
函数的定义是可以嵌套的,就会出现嵌套的作用域。
var scope = "global scope"; // 全局变量
function checkscope() {
var scope = "local scope"; // 局部变量
function nested() {
// 嵌套的局部变量
var scope = "nested scope";
return scope; // 返回嵌套的局部变量
}
return nested();
}
document.writeln(checkscope()); // => "nested scope"
2.1函数作用域和声明提前
在一些类C的编程语言中,花括号中的每一段代码都有自己的作用域,其中的变量在花括号之外的是看不见的,我们称之为块级作用域。在javascript中是没有块作用域的,取而代之的是函数作用域:
变量在声明它们的函数体以及整个函数体嵌套的任意函数体都是有意义的。
function test(o) {
var i = 0; // i 在函数体内都可以访问
if (typeof o == "object") {
var j = 0; // j 在函数体内都也可以访问
for(var k=0; k < 10; k++) { // k 在函数体内都也可以访问
console.log(k); // 打印0-9
}
console.log(k); // 在此位置也可以访问k: 输出10
}
console.log(j); // j已经定义,可能没有初始化
}
test({});
test(1);
函数体中声明的所有变量在函数体内任意位置始终都是可见的。这意味着在变量声明之前就是可用的。这就是声明提前,即javascript函数声明里的所有变量(但不涉及到赋值)都被“提前”到函数的顶部。
var scope = "global";
function f() {
console.log(scope); // 输出"undefined", 而不是 "global"
var scope = "local"; // scope在此位置被初始化, 但是之前就已经可用
console.log(scope); // 输出"local"
}
//上述代码类似于
var scope = "global";
function f() {
var scope; // 在函数顶部声明变量
console.log(scope); // 输出"undefined", 而不是 "global"
var scope = "local"; // scope在此位置被初始化, 但是之前就已经可用
console.log(scope); // 输出"local"
}
所以最好将所有的变量声明在函数顶部,这样能很好的表示变量的作用域。
2.2作为属性的变量
当一个变量声明为全局变量时,实际上是定义了全局对象的一个属性。当使用var声明一个变量时, 创建的这个变量是不可配置,也就是说无法通过delete运算符删除。
如果你给一个未声明的变量赋值,javascript会自动创建一个全局变量,这个全局变量是可配置的。
var test = "12";
delete test;
console.log(test);//12
str = "sss";
delete str;
console.log(str);//ReferenceError: str is not defined
全局变量是全局对象的属性,这是ECMAScript强制规定的。对于局部变量没有此规定,但我们可以想象得到,把局部变量当做跟函数调用相关的某个对象的属性。ECMAScript 3规范称该对象为“调用对象”(call object),ECMAScript 3规范称为“声明上下文对象”(declarative environment record)。javascript允许使用this关键字来引用全局对象,但是没有方法引用局部变量。
3.3作用域链
如果将局部变量看做是自定义实现的对象的属性的话,那么可以换个角度解读变量作用域。每一段javascript代码(全局代码或函数)都有一个与之相关联的作用域链(scope chain)。这个作用域链是一个对象列表或链表,这组对象定义了这段代码“作用域中的变量”。当javascript需要查找变量X的值时,它会从链中的第一个对象开始查找,有就使用,没有就继续,如果作用域链中没有一个对象有x属性,则抛出引用异常:ReferenceError。
在javascript的最顶层代码中(不在任何函数体内的代码),作用域由一个全局对象组成。在不包含嵌套的函数体中,作用域链上有两个对象,第一个是定义函数参数和局部变量的对象,第二个是全局对象。而包含嵌套的函数体中,对于嵌套函数来说,作用域链上至少有三个对象,第一个是嵌套函数自己的参数和局部变量对象,其次是外部函数的,最后是全局对象。
当定义一个函数时,它实际保存一个作用域链。当调用这个函数时,它创建一个新的对象来存储它的局部变量,并将这个对象添加至保存的那个作用域链,同时创建一个新的更长的表示函数调用作用域的“链”。
对于嵌套函数来说,每次调用外部函数,内部函数都会重新定义一遍,这是因为每次调用函数,作用域链都是不一样的。内部函数在每次定义时都有微妙的差别——在每次调用外部函数时,内部函数代码都是相同的,但是关联这段代码的作用域链是不同的。