作用域与变量提升
一、作用域
1.函数作用域:变量在声明它们的函数体以及在嵌套这个函数体内都是有定义的。
先看一段代码
var scope="global"; function t(){ console.log(scope); //undefined var scope="local" console.log(scope); //local } t();
这里涉及到了变量提升,变量初始化后,js会将变量的声明提升到函数顶部,如果是函数外部的全局变量,则提升到js顶部(作用域顶部)
所以这段代码可以转化成
demo
var scope="global"; function t(){ var scope console.log(scope); //undefined scope="local" console.log(scope); //local } t(); `` // 又比如,定义三个变量 demo3 `` (function(){ var a='One'; var b='Two'; var c='Three'; })() ; `` // 实际上它变量提升后是这样的: `` (function(){ var a,b,c; a='One'; b='Two'; c='Three'; })()
``
##需要注意一点的是,函数有两种声明形式,一种是函数声明式,一种是函数表达式式,只有函数声明式中,变量才会被提升。
2.JS没有块级作用域
为什么这么说呢?我们可以看以下代码
demo1
``
var name="global"; if(true){ var name="local"; console.log(name) //local } console.log(name); //local
``
如果说函数有块级作用域的话,那么if判断语句中的代码块应该是有自己的作用域的。但是这里的两个name打印的都是local,这是为什么呢?
第一个name是属于函数内部变量,所以固然打印的是local;
第二个name打印的其实也是local,因为var name="local"已经将函数语句外部的var name="global"覆盖。
二、变量作用域
1.js中未用var声明的变量,属于隐式全局变量,也是顶层对象的属性
我们看一个实例
demo
``
function t(flag){ if(flag){ s="ifscope"; for(var i=0;i<2;i++) ; } console.log(i); } t(true); console.log(s);
``
这段代码,外部的console.log也可以访问函数内部的s,就是因为s没有用var声明,属于全局变量,函数外部也可以访问到,所以console.log(s)的效果相当于console.log(window.s)。
2.当使用var声明一个变量时,变量无法使用delete运算符删除
var name=1 ->不可删除
sex=”girl“ ->可删除
this.age=22 ->可删除
三、作用域链
1.demo:
``
name="shark"; function t(){ var name="jan"; function s(){ var name="jack"; console.log(name); } function ss(){ console.log(name); } s(); //jack ss(); //jan } t();
``
调用对象时,js会把对象放在作用域链开头,然后将函数的调用放在之后,最后是全局对象,知道找到name为止。
上面的例子中,
s(),很明显指定是打印s()函数中的name,结果是jack,
ss(),ss内部没有name属性,那么它会到它的上一层函数中去寻找,找到name="jan",则会打印出来jan。
2.下面看一个很容易犯错的例子:
demo
``
<html> <head> <script type="text/javascript"> function buttonInit(){ for(var i=1;i<4;i++){ var b=document.getElementById("button"+i); b.addEventListener("click",function(){ alert("Button"+i);},false); } } window.onload=buttonInit; </script> </head> <body> <button id="button1">Button1</button> <button id="button2">Button2</button> <button id="button3">Button3</button> </body> </html>
``
看了上面的代码,我们很容易会想到,结果分别打印出Button1,Button2,Button3。但是事实呢?
##注意这里点击事件里的匿名函数并没有i这个变量,所以它会到上一层函数buttonInit中去找,而注册事件结束后,i的值已经是4了,所以这里匿名函数其实是
function(){ alert("Button"+4);
所以三个按钮打印的都是Button4。