一,每一个变量都是有作用域的。
1,首先讲一下代码块与作用域:for, if等语句还有function,他们都有一个特点,就是后面有一对{}比如
for(var i = 0; i < items.length; i++){
//“{”和“}”之间有代码
}
if(some_express){
//“{”和“}”之间有代码
}
function(param1){
//“{”和“}”之间有代码
}
所有{}包起来的代码,都可以称之为 “代码块” , 英文名叫Block。
每个代码块都有一个自己的作用域,作用域决定了变量能否被访问(不论读取变量还是修改变量)。
访问的规则大概是这样的:
[1] 在最上层没有任何{}包裹的作用域为顶层作用域,声明的变量是哪里都可以访问的,如:
var x = 5;
if(x>0){ //可以访问
x = 6; //可以访问
}
function print_x(){
console.log(x); //也可以访问
}
[2] 任何一个代码块,{}之间的代码区域称之为它的作用域,每一对大括号括起来的代码块里声明的变量,只能在这个代码块的作用域里访问,不过在js里是不具备这个能力的。 但是为了建立起正确的思维观,我们还是要讲讲块级作用域。虽然语言不支持,但我们人不要跨越块级作用域,这是一个基本素质,会极大的减少Bug发生率。
if(true) { var x = 5; console.log(x); //可以访问 } console.log(x); //其他语言里无法访问,但是在js里可以
[3] 代码块是有层级的,在一个代码块里写的新的代码块,后者是前者的子作用域
if(true){ //相对于下面的代码块的父作用域 var x = 5; if(x > 0){ //子作用域 } }
[4] 子作用域可以访问父作用域的变量,但是父作用域无法访问子作用域的变量
if(true){ var x = 5; if(x > 0){ var y = "a"; console.log(x); //可以访问 x = 6; //也可以访问 } console.log(y); //不应该在这里再访问了。 }
[5] 作用域的父级的父级作用域的变量,该作用域里也可以访问,甚至无穷多级父的作用域里的变量都可以被访问,这种无穷多级的父,被称之为祖先
if(true){ var x = 5; if(x > 0){ console.log(x); //可以访问 for(var i = 0; i < x; i++){ console.log(x); //也可以访问 } } }
[6] 顶层作用域是所有作用域的祖先
2,函数的作用域
函数比起if,for等代码块有一个显著地不同,就是他可以在参数列表里声明变量
function a(param1, param2){ //这里就可以使用param1和param2了 }
在父作用域声明的变量,也可以在函数内被访问
var x = 5; function a(param1){ console.log(x); //可以访问到,打印的是5 }
关于作用域的覆盖问题,当子作用域有变量与父作用域重名的时候,在子作用域里只能访问到子作用域的变量。称之为 覆盖。
var x = 5;
function a(x){
console.log(x); //这时访问的x就不是父作用域的x了,而是参数x
}
a(6); //打印的是6
console.log(x); //打印的是5
所以,子作用域的覆盖不会消除父作用域的变量
var x = 5;
function a(x){
console.log(x); //这时访问的x就不是父作用域的x了,而是参数x
}
a(6); //打印的是6
console.log(x); //打印的是5
例如:
var x = 5;
function a(x){
if(x > 0){
x++;
}
console.log(x);
}
a(1);
console.log(x);
代码的最后一行输出结果是5. //因为覆盖并不会改变父作用域的变量。
3,要注意一些容易犯的错误:
for(var i = 0; i < 3; i++){
var result = [];
result.push(i);
}
console.log(result); //有的初学者以为这里会打印 [0,1,2],实际上会打印[2],因为result是在for里面被定义的, 每次循环都被重新初始化了
关于覆盖也是容易出错误:
var x = 0;
function fun(){
console.log(x);
var x = 1;
x++;
}
fun();
我们想先用一下全局变量x,然后再在子作用域里声明一个重名的局部变量x。结果我们会发现console.log()打印出的是 undefined。 因为js虽然是解释执行的,但是在一个作用域里声明的变量在这个作用域里只有一个指向,即便还没有执行到正式声明的哪一行,父级作用域的变量也已经因覆盖无法访问了 二,为了避免一些不必要的错误,我们在书写代码的时候要注意规范命名,养成好的习惯,提高写代码的效率。