在函数体内部,局部变量的优先级别高于同名的全局变量。如果函数外部和内部均有没有使用关键字var声明的同名变量,那么这个变量将被修改为函数内部的那个变量。
当函数内部的变量未使用var声明时,就相当于声明了一个全局变量,那么这样的话,函数内部的变量a就覆盖了函数外部的变量a,因为最后的结果:变量a的值为“Bob”,变量b的值为“Bob”。
若此时,把代码稍微修改一下:
可以发现,在函数curr没有执行之前,变量a的值还是"Tom",执行之后,a的值为"Bob"。
JavaScript的函数作用域是指:在函数内部声明的变量在函数体中是随处可见的。意味着,在函数体中变量未声明之前就可用,这就是JavaScript的特性“声明提前”,这个提前是“这是声明”的提前,而不是“这是赋值”的提前,也就是说,在执行函数体的代码之前,已经将“变量声明”这一步骤提前到了函数体顶部,而并未赋值。
在函数体的第一行中,输出的是"undefined",而不是认为的“Tom”,因为此时已经将变量a的声明"var a;"提前在了函数体顶部,但并未赋值,所以输出的是"undefined",此时的a表示的是函数内部的变量a。前面说过:在函数内部,局部变量的优先级别高于全局变量。
以上代码被引擎解析为:
这样,是否好理解一些。
注:从以上可以看出,在以后的编程中,尽量将函数内部中要用到的变量的声明放在函数体的顶部,这样就能更好地反映真实的变量作用域。
在JavaScript中,在全局作用域下声明的变量是作为全局对象的属性的。当使用关键字var声明的变量是不可使用delete操作符删除的,而未使用var直接赋值的变量是可以通过delete操作符删除的。
在函数内部声明的局部变量是作为 跟函数调用时相关的对象的 属性,称为“声明上下文对象”,也称“调用对象”。
在JavaScript中,允许this对象引用全局变量,却没有办法引用局部变量。
当定义一个函数时,它保存着一个作用域链,当调用这个函数时,它将新创建一个对象用来保存局部变量,并将这个对象保存在那个作用域链上,同时新创建一个表示 函数调用作用域的“链”。对于嵌套函数来说,每次调用外部函数时,作用域链也不同,内部函数都会重新定义一遍,因为每次调用外部函数,虽然内部函数的代码一样,但关联这代码的作用域链不同。
a = "Tom";
function curr() {
a = "Bob";
return a;
}
var b = curr();//调用函数curr
console.log(a); //Bob
console.log(b); //Bob
当函数内部的变量未使用var声明时,就相当于声明了一个全局变量,那么这样的话,函数内部的变量a就覆盖了函数外部的变量a,因为最后的结果:变量a的值为“Bob”,变量b的值为“Bob”。
若此时,把代码稍微修改一下:
a = "Tom";
function curr() {
a = "Bob";
return a;
}
console.log(a); //Tom
var b = curr();//调用函数curr
console.log(a); //Bob
console.log(b); //Bob
可以发现,在函数curr没有执行之前,变量a的值还是"Tom",执行之后,a的值为"Bob"。
变量作用域
JavaScript的函数作用域是指:在函数内部声明的变量在函数体中是随处可见的。意味着,在函数体中变量未声明之前就可用,这就是JavaScript的特性“声明提前”,这个提前是“这是声明”的提前,而不是“这是赋值”的提前,也就是说,在执行函数体的代码之前,已经将“变量声明”这一步骤提前到了函数体顶部,而并未赋值。
var a = "Tom";
function curr() {
console.log(a); //undefined
var a = "Bob";
console.log(a); //Bob
}
curr();
在函数体的第一行中,输出的是"undefined",而不是认为的“Tom”,因为此时已经将变量a的声明"var a;"提前在了函数体顶部,但并未赋值,所以输出的是"undefined",此时的a表示的是函数内部的变量a。前面说过:在函数内部,局部变量的优先级别高于全局变量。
以上代码被引擎解析为:
var a = "Tom";
function curr() {
var a;
console.log(a); //undefined
a = "Bob";
console.log(a); //Bob
}
curr();
这样,是否好理解一些。
注:从以上可以看出,在以后的编程中,尽量将函数内部中要用到的变量的声明放在函数体的顶部,这样就能更好地反映真实的变量作用域。
作为属性的变量
在JavaScript中,在全局作用域下声明的变量是作为全局对象的属性的。当使用关键字var声明的变量是不可使用delete操作符删除的,而未使用var直接赋值的变量是可以通过delete操作符删除的。
var a = 123; //这是不可删除的变量
b = 234; //这是可以删除的变量
this.a1 = "hello"; //也是可以删除的变量
delete a;
console.log(a); //123
delete b;
console.log(b); //报错 b未声明
delete a1;
console.log(a1); //报错
在函数内部声明的局部变量是作为 跟函数调用时相关的对象的 属性,称为“声明上下文对象”,也称“调用对象”。
在JavaScript中,允许this对象引用全局变量,却没有办法引用局部变量。
作用域链
我们知道,全局变量在程序中始终有定义的,局部变量在声明它的函数体中或嵌套在声明它的函数中的函数内始终有定义的。如果将局部变量看作是自定义实现的对象的 属性 的话,那么更加容易理解变量作用域。JavaScript中的每一段代码(全局代码或函数),都有一个与之相关的“作用域链”,这个作用域链是对象列表或链列表,这组对象定义了这段代码“作用域链”中的变量。JavaScipt需要查找变量x的时候,就会先查找作用域链上的第一个对象是否有这个属性(变量x),如果有则使用这个变量,如果没有,则继续查找下一个对象看是否有这个变量,如果也没有,则继续查找下一个对象,依次类推,逐级向上查找,直至查找到全局作用对象为止。如果最后这个作用域链中的所有对象都没有这个属性(变量x)的话,程序会抛出一个引用错误。
当定义一个函数时,它保存着一个作用域链,当调用这个函数时,它将新创建一个对象用来保存局部变量,并将这个对象保存在那个作用域链上,同时新创建一个表示 函数调用作用域的“链”。对于嵌套函数来说,每次调用外部函数时,作用域链也不同,内部函数都会重新定义一遍,因为每次调用外部函数,虽然内部函数的代码一样,但关联这代码的作用域链不同。