js做用域与做用域链

一直对Js的做用域有点迷糊,今天偶然读到JavaScript权威指南,立马被吸引住了,写的真不错。我看的是第六版本,至关的厚,大概1000多页,Js博大精深,要熟悉精通须要大毅力大功夫。javascript

一:函数做用域html

   先看一小段代码:java

[javascript]  view plain  copy
  1. var scope="global";  
  2. function t(){  
  3.     console.log(scope);  
  4.     var scope="local"  
  5.     console.log(scope);  
  6. }  
  7. t();  

 

(PS: console.log()是firebug提供的调试工具,很好用,有兴趣的童鞋能够用下,比浏览器+alert好用多了)浏览器

第一句输出的是: "undefined",而不是 "global"app

第二讲输出的是:"local"函数

  你可能会认为第一句会输出:"global",由于代码还没执行var scope="local",因此确定会输出“global"。工具

  我说这想法彻底没错,只不过用错了对象。咱们首先要区分Javascript的函数做用域与咱们熟知的C/C++等的块级做用域。oop

  在C/C++中,花括号内中的每一段代码都具备各自的做用域,并且变量在声明它们的代码段以外是不可见的。而Javascript压根没有块级做用域,而是函数做用域.this

所谓函数做用域就是说:-》变量在声明它们的函数体以及这个函数体嵌套的任意函数体内都是有定义的。spa

因此根据函数做用域的意思,能够将上述代码重写以下:

 

[javascript]  view plain  copy
  1. var scope="global";  
  2. function t(){  
  3.     var scope;  
  4.     console.log(scope);  
  5.     scope="local"  
  6.     console.log(scope);  
  7. }  
  8. t();  

    咱们能够看到,因为函数做用域的特性,局部变量在整个函数体始终是由定义的,咱们能够将变量声明”提早“到函数体顶部,同时变量初始化还在原来位置。

为何说Js没有块级做用域呢,有如下代码为证:

 

[javascript]  view plain  copy
  1. var name="global";  
  2. if(true){  
  3.     var name="local";  
  4.     console.log(name)  
  5. }  
  6. console.log(name);  

都输出是“local",若是有块级做用域,明显if语句将建立局部变量name,并不会修改全局name,但是没有这样,因此Js没有块级做用域。

 

 

如今很好理解为何会得出那样的结果了。scope声明覆盖了全局的scope,可是尚未赋值,因此输出:”undefined“。

因此下面的代码也就很好理解了。

 

[javascript]  view plain  copy
  1. function t(flag){  
  2.     if(flag){  
  3.         var s="ifscope";  
  4.         for(var i=0;i<2;i++)   
  5.             ;  
  6.     }  
  7.     console.log(i);  
  8.     console.log(s);  
  9. }  
  10. t(true);  

输出:2  ”ifscope"


 

二:变量做用域

仍是首先看一段代码:

 

[javascript]  view plain  copy
  1. function t(flag){  
  2.     if(flag){  
  3.         s="ifscope";  
  4.         for(var i=0;i<2;i++)   
  5.             ;  
  6.     }  
  7.     console.log(i);  
  8. }  
  9. t(true);  
  10. console.log(s);  


就是上面的翻版,知识将声明s中的var去掉。

 

程序会报错仍是输出“ifscope"呢?

让我揭开谜底吧,会输出:”ifscope"

这主要是Js中没有用var声明的变量都是全局变量,并且是顶层对象的属性。

因此你用console.log(window.s)也是会输出“ifconfig"

 

当使用var声明一个变量时,建立的这个属性是不可配置的,也就是说没法经过delete运算符删除

var name=1    ->不可删除

sex=”girl“         ->可删除

this.age=22    ->可删除

 

三:做用域链

先来看一段代码:

 

[javascript]  view plain  copy
  1. name="lwy";  
  2. function t(){  
  3.     var name="tlwy";  
  4.     function s(){  
  5.         var name="slwy";  
  6.         console.log(name);  
  7.     }  
  8.     function ss(){  
  9.         console.log(name);  
  10.     }  
  11.     s();  
  12.     ss();  
  13. }  
  14. t();  


当执行s时,将建立函数s的执行环境(调用对象),并将该对象置于链表开头,而后将函数t的调用对象连接在以后,最后是全局对象。而后从链表开头寻找变量name,很明显

 

name是"slwy"。

但执行ss()时,做用域链是: ss()->t()->window,因此name是”tlwy"

下面看一个很容易犯错的例子:

 

[html]  view plain  copy
  1. <html>  
  2. <head>  
  3. <script type="text/javascript">  
  4. function buttonInit(){  
  5.     for(var i=1;i<4;i++){  
  6.         var b=document.getElementById("button"+i);  
  7.         b.addEventListener("click",function(){ alert("Button"+i);},false);  
  8.     }  
  9. }  
  10. window.onload=buttonInit;  
  11. </script>  
  12. </head>  
  13. <body>  
  14. <button id="button1">Button1</button>  
  15. <button id="button2">Button2</button>  
  16. <button id="button3">Button3</button>  
  17. </body>  
  18. </html>  

当文档加载完毕,给几个按钮注册点击事件,当咱们点击按钮时,会弹出什么提示框呢?

 

很容易犯错,对是的,三个按钮都是弹出:"Button4",你答对了吗?

当注册事件结束后,i的值为4,当点击按钮时,事件函数即function(){ alert("Button"+i);}这个匿名函数中没有i,根据做用域链,因此到buttonInit函数中找,此时i的值为4,

因此弹出”button4“。

 

四:with语句

说到做用域链,不得不说with语句。with语句主要用来临时扩展做用域链,将语句中的对象添加到做用域的头部。

看下面代码

[javascript]  view plain  copy
  1. person={name:"yhb",age:22,height:175,wife:{name:"lwy",age:21}};  
  2. with(person.wife){  
  3.     console.log(name);  
  4. }  

with语句将person.wife添加到当前做用域链的头部,因此输出的就是:“lwy".

 

with语句结束后,做用域链恢复正常。

相关文章
相关标签/搜索