Nigel_Woo

JS与Jquery学习笔记(一)

 

一. Javascript的作用域,大坑!

  1. JS作用域奇怪表现之一:预编译

在其他的语言里我们如果使用一个变量在声明其之前,是会报错的,但是在js里面却不一定,比如:

1 function f1() {
2     console.log(aaa);
3     var aaa = 1;
4 }
5 
6 f1();

这里解释器就不会报错,而是会输出一个undefined出来,也就是声明了但是没有初始化赋值的效果。原有在于js的解释器从上到下执行,到f1这里的时候虽然不会执行,但是已经把函数放到内存里了,顺便aaa也在内存里了(相当于已经有个 var aaa;了)。但是如果彻底没能让解释器找到个变量还是会报错的,比如:

function f1() { 
    console.log(aaa); 
} 
f1();

  

  2. JS没有块级作用域!

我们知道在其他语言中,如果在一个块级空间(比如if语句的内部、for语句的内部等等)内定义一个局部变量,其作用域就只在这个块内,出了块以后就没有了,但是JS并不是这样,它没有块级的作用域,其作用域是以function为单位的(貌似不少脚本语言都酱紫?比如python...),只要在一个function里面定义的,即使出了一些块也是能找到的,比如:

1 function f1(){
2     if (1 === 1){
3         var aa = 123;
4     }
5     console.log(aa);
6 }

这样是能够正确输出结果123的。

再来看个行为诡异的例子:

1 function f1(){
2     for(var i=0; i<10; i++){
3         
4     }
5     console.log(i);
6 }
7 f1();

例子的结果是10!赶脚和其他语言真是差距太大了...原因是首先function内是同一作用域所以能够输出i而非undefined,此外由于真正退出for循环的时候,其实i已经循环加到10了,所以这才是其的值。

 然后继续看更诡异的例子...

1 function f1(){
2     for(var i=0; i<3; i++){
3         setInterval(function (){
4             console.log(i);
5         }, 1000);
6     }
7 }
8 f1();

最后定时器输出的全部都是3。这是因为在调用f1的时候,循环完创建3个定时器并给它们绑定匿名函数的后,退出循环的时候,i已经是3了,而此时用于输出的匿名函数还并未运行。直到定时器启动它们,匿名函数开始从它们自己的作用域链开始寻找,其函数内部没有变量i,就找到了f1函数内部的变量i,而此时i已经是3了。所以之后输出的都是3。也许像下面这样分开来不用匿名函数看起来逻辑更清晰:

1 function f1(){
2     for(var i=0; i<3; i++){
3         function f2 (){
4             console.log(i);
5         }
6         setInterval(f2, 1000);
7     }
8 }
9 f1();

这样看起来setInterval就和分开了。

再看一个类似的例子,先在html中搞3个按钮:

1 <div id="btns">
2     <input type="button" value="btn1"/>
3     <input type="button" value="btn2"/>
4     <input type="button" value="btn3"/>
5 </div>

然后在js里面给三个按钮都绑定上点击事件:

1 var btns = document.getElementById('btns').children;
2 for (var i=0; i< btns.length; i++){
3     btnbtn = btns[i];
4     btnbtn.onclick = function (){
5         console.log(i);
6     };
7 }

这样最后按任何按钮输出的也都是3,原理和之前一样,即循环绑定的时候,你们函数并未执行,而当点击执行的时候,i已经是3了。

 

  3. JS的作用域链

先看一个例子:

 1 aa = 1;
 2 function f1(){
 3     var aa = 2;
 4     function f2() {
 5         alert(aa);
 6     }
 7     return f2;
 8 }
 9 var ret = f1();
10 ret();

这个例子的输出是2而非1;这里的结果和python里的闭包非常相似;因为JS里面有个作用域链。以上面的例子为例,链的顺序是从全局->外层函数f1->内层函数f2,

当运行的时候要查找变量,就会逆着这个链,从最内层向外找;此处因为在定f2中找不到aa,就找到了f1中的aa,而f1中的aa值是之前执行f1函数时候里面已经定义好的了,所以就是2了;此时已经找到aa,就不会继续往上找全局的aa的值了。再看个例子:

 1 aa = 1;
 2 function Bar(){
 3     console.log(aa);
 4 }
 5  
 6 function Func(){
 7     var aa = 2;
 8     return Bar;
 9 }
10  
11 var ret = Func();
12 ret();

这里的结果是1!原有是其实这部分代码有两个作用域链,函数Bar的作用域链就是全局->Bar,而Func的是全局->Func,需要注意的是Bar是定义在全局里的,虽然在Func里面return了Bar,但是它的作用域还是外面的!所以虽然执行Func返回的Bar,Func内的aa的值为2,但是真正运行ret()来执行Bar函数的时候,寻找aa的路径还是从Bar内部,然后直接就找到全局里面的aa了,所以结果为1.

 

 

 

 

 

施工中。

posted on 2016-08-13 16:16  Nigel_Woo  阅读(127)  评论(0编辑  收藏  举报

导航