setTimeout與console.log()執行順序,局部變量和全局變量,var變量提高,this指向, 看不懂你來打我

使人心痛的血淋淋教訓,再犯這些錯誤我不是人。瀏覽器

setTimeout與console.log()執行順序

setTimeout延時爲0時,
    setTimeout(function(){
        console.log(2);
    },0);
    console.log(1);
    //輸出順序:1,2
    
     setTimeout(function(){
        console.log(4);
    },0);
    setTimeout(function(){
        console.log(5);
    },0);
    console.log(1);
    console.log(2);
    console.log(3);
    //輸出順序:1,2,3,4,5

緣由:(記住嘍,記不住打死你!!!):頁面中全部由setTimeout定義的操做,都將放在同一個隊列中依次執行。而這個隊列的執行時間須要等到函數調用棧執行完畢後纔會執行,也就是等待全部的可執行代碼執行完畢,纔會輪到setTimeout執行其內部操做,而且按照其時延時間長短順序執行代碼!閉包

再來個高深的:瞅着函數

setTimeout(function(){
        console.log("a:"+a);
    },0);
    var a = 1;
    console.log("b:"+b);
    var b = 2;
    var c = 3;
    var d = 4;
    var e = 5;
    function fx(c){
        console.log("c:"+c);
    }
    function fn(e,d){
        console.log("d:"+d);
        setTimeout(function(){
            console.log("e:"+e);
        },10);
    }
    setTimeout(function(){
        console.log("b2:"+b);
    },20);
    fn(e,d);
    fx(c);

輸出結果:
clipboard.pngthis

緣由:
一、console.log()函數會在setTimeout函數以前執行,而且b在輸出以前未被定義因此最早輸出undefined;
二、以後,會執行fn函數和fx函數,而fn函數內存在console.log函數,那麼它將會先輸出d的值4;
三、而後,在fx函數內也存在console.log函數,一樣會先輸出c的值3;
四、再來比較setTimeout函數時延長短,依次輸出1,5,2。

3)spa

for (var i = 0; i < 3; i++) {
setTimeout(function() {
    console.log(i);
}, 0);
console.log(i);
}
//0 1 2 3 3 3
用到了閉包

4)code

for (var i = 0; i < 3; i++) {}
console.log(i); 
//3,也就說i能夠在for循環體外訪問到。因此是沒有塊級做用域。

5)對象

var i = 0;
setTimeout(function() {
    console.log(i);
}, 0);
console.log(i);
i++;
setTimeout(function() {
    console.log(i);
}, 0);
console.log(i);
i++;
setTimeout(function() {
    console.log(i);
}, 0);
console.log(i);
i++;    

等價於:
var i = 0;
console.log(i);
i++;
console.log(i);
i++;
console.log(i);
i++;
setTimeout(function() {
    console.log(i);
}, 0);
setTimeout(function() {
    console.log(i);
}, 0);
setTimeout(function() {
    console.log(i);
}, 0);  //彈出 0 1 2 3 3 3

全局變量和局部變量的區別

1)blog

var a = "Hello";
    function test(){
        var a;
        alert(a);
        a = "World";
        alert(a);
    }
    test();//undefined world

2)隊列

var a = "Hello";
    function test(){
        alert(a);
        a = "World";
        alert(a);
    }
    test();//Hello World

3)ip

var a =1;
    function test(){
        alert(a);
        var a = 2;
        alert(a);
    }
    test();
    alert(a); //undefined 2 1
  • Javascript的變量的scope是根據方法塊來劃分的(也就是說以function的一對大括號{ }來劃分)。是function塊,而for、while、if塊並非做用域的劃分標準,能夠看看如下幾個例子:

    function test2(){      
       alert ("before for scope:"+i);    
          // i未賦值(並非未聲明!使用未聲明的變量或函數全拋出致命錯誤而中斷腳本執行)       // 此時i的值是underfined      
       for(var i=0;i<3;i++){          
           alert("in for scope:"+i);  // i的值是 0、一、2, 當i爲3時跳出循環      
           }     
       alert("after for scope:"+i);  // i的值是3,注意,此時已經在for scope之外,但i的值仍然保留爲3            
       while(true){          
           var j = 1;          
           break;      
           }      
           alert(j);    
           // j的值是1,注意,此時已經在while scope之外,但j的值仍然保留爲1        
           if(true){          
               var k = 2;      
               }      
           alert(k);  //k的值是1,注意,此時已經在if scope之外,但k的值仍然保留爲1  
       }  
       test2();  
       //若在此時(function scope以外)再輸出只存在於test2 這個function scope裏的 i、j、k變量會發生神馬效果呢?  
       alert(i); //error! 沒錯,是error,緣由是變量i未聲明(並非未賦值,區分test2函數的第一行輸出),致使腳本錯誤,程序到此結束!  
       alert("這行打印還會輸出嗎?"+i); //未執行  
       alert(j); //未執行  
       alert(k); //未執行
  • Javascript在執行前會對整個腳本文件的聲明部分作完整分析(包括局部變量),從而肯定實變量的做用域。怎麼理解呢?看下面一個例子:

    var a =1;     
     function test(){          
          alert(a); //a爲undefined! 這個a並非全局變量,這是由於在function scope裏已經聲明瞭(函數體倒數第4行)一個重名的局部變量,                       
          //因此全局變量a被覆蓋了,這說明了Javascript在執行前會對整個腳本文件的定義部分作完整分析,因此在函數test()執行前,                       
          //函數體中的變量a就被指向內部的局部變量.而不是指向外部的全局變量. 但這時a只有聲明,還沒賦值,因此輸出undefined。          
          a=4;                 
           alert(a);  //a爲4,沒懸念了吧? 這裏的a仍是局部變量哦!          
           var a;     //局部變量a在這行聲明          
           alert(a);  //a仍是爲4,這是由於以前已把4賦給a了      
           }      
           test();      
           alert(a); //a爲1,這裏並不在function scope內,a的值爲全局變量的值 
  • 當全局變量跟局部變量重名時,局部變量的scope會覆蓋掉全局變量的scope,當離開局部變量的scope後,又重回到全局變量的scope,而當全局變量趕上局部變量時,怎樣使用全局變量呢?用window.globalVariableName。

    var a =1;      
       function test(){             
           alert(window.a);  //a爲1,這裏的a是全局變量哦!          
           var a=2;     //局部變量a在這行定義          
           alert(a);  //a爲2,這裏的a是局部變量哦!      
           }      
      test();      
      alert(a); //a爲1,這裏並不在function scope內,a的值爲全局變量的值

**總結:(每一個例子慢慢看,就全懂了)
一、js有兩級做用域,全局和局部,局部也叫函數做用域
二、全局做用域的變量局部可使用,局部做用域的變量只能在函數體內使用
三、var和function聲明的變量都聲明提早,賦值留在原地
四、若是局部和全局變量重名,優先使用局部變量
五、第3條和第4條,解釋了全局和局部都有相同變量名的時候,而在函數體內打印的變量是undefined**

var變量提高(這個問題我一直只知其一;不知其二,如今懂了)

通常狀況下,是能夠省略var的,但有兩點值得注意:
一、var a=1 與 a=1 ,這兩條語句通常狀況下做用是同樣的。可是前者不能用delete刪除。不過,絕大多數狀況下,這種差別是能夠忽略的。
二、在函數內部,若是沒有用var 進行申明,則建立的變量是**全局變量,而不是局部變量**了。因此,建議變量申明加上var關鍵字。

瞅着:
1)

var t = 1; 
    function a(){
        console.log(t);
        var t=2;
    }
    a();//undefined;

輸出undefined,緣由:function()中至關於

var t;
console.log(t);
t = 2;
表示變量t已聲明,但還未賦值,輸出undefined。

2)

可是變量提高只對var命令聲明的變量有效,若是一個變量不是用var命令聲明的,就不會發生變量提高。
console.log(aa);
aa =1;
以上代碼將會報錯:ReferenceError: aa is not defined。

3)

var t = 1;
    function a(){
        console.log(t);
        t=2;
    }
    a();//1

4)

函數聲明變量提高
foo();
function foo(){
    console.log("aaa");
}
//輸出aaa

緣由:函數聲明提高 (函數聲明提高直接把整個函數提到執行環境的最頂端)
它至關於
function foo(){
    console.log("aaa");
}
foo();

5)

foo();
var foo = function(){
    console.log("aaa");
} 
它至關於:
var foo;
foo(); //foo is not a function
foo = function(){
        console.log("aaa");
    }          
上面代碼輸出undefined 是由於變量提高後並無賦值所以輸出undefined

輸出foo is not a function 緣由是:js解析遇到 foo()時會默認當作函數來解析

6)最厲害的一個

console.log(foo);
var foo=10;
console.log(foo);
function foo(){
    console.log(10);
}
console.log(foo);

他至關於:
function foo(){
    console.log(10);
}
var foo;
console.log(foo);
foo=10;
console.log(foo);
console.log(foo);

clipboard.png

函數提高在變量提高上面,第一個console.log(foo);爲何會輸出函數題呢,緣由在於 var foo; 並未有賦值只是聲明,所以他會調用上面的值.

this指向

  1. 全局環境中,this指向window
       console.log(this.document === document); // true
       
       // 在瀏覽器中,全局對象爲 window 對象:
       console.log(this === window); // true
       
       this.a = 37;
       console.log(window.a); // 37
  2. 函數調用
    非嚴格模式下,this 默認指向全局對象window

    function f1(){
         return this
       f1() === window; // true
    
       而嚴格模式下, this爲undefined
       function f2(){
         "use strict"; // 這裏是嚴格模式
         return this;
       }    
       f2() === undefined; // true
  3. 1)

    對象中的this
         var o = {
         user:"李鋼鐵",
         fn:function(){
               console.log(this.user);  //李鋼鐵
           }
       }
       o.fn();

    這裏的this指向的是對象o,由於你調用這個fn是經過o.fn()執行的,那天然指向就是對象o,這裏再次強調一點,this的指向在函數建立的時候是決定不了的,在調用的時候才能決定,誰調用的就指向誰,必定要搞清楚這個。

2).

var o = {
user:"李鋼鐵",
fn:function(){
    console.log(this.user); //李鋼鐵
}
}
window.o.fn();
this指向o

3).

var o = {
  a:10,
  b:{
        a:12,
        fn:function(){
            console.log(this.a); //12
        }
    }
}
o.b.fn();

總結:若是一個函數中有this,這個函數有被上一級的對象所調用,那麼this指向的就是上一級的對象。

4).

var o = {
a:10,
b:{
    // a:12,
    fn:function(){
        console.log(this.a); //undefined
    }
   }
}
o.b.fn();
this指向b,由於this只會指向它的上一級對象,無論這個對象中有沒有this要的東西。

5).

特殊狀況
var o = {
a:10,
b:{
    a:12,
    fn:function(){
        console.log(this.a); //undefined
        console.log(this); //window
    }
}
}
var j = o.b.fn;
j();

這裏this指向的是window,,this永遠指向的是最後調用它的對象,也就是看它執行的時候是誰調用的,例子4中雖然函數fn是被對象b所引用,可是在將fn賦值給變量j的時候並無執行因此最終指向的是window。

4.

構造函數中this
function Fn(){
this.user = "李鋼鐵";
    }
var a = new Fn();
console.log(a.user); //李鋼鐵
指向實例化對象a

當this遇到return時,若是返回值是一個對象,那麼this指向的就是那個返回的對象,若是返回值不是一個對象那麼this仍是指向函數的實例。

function fn()  
{  
    this.user = '李鋼鐵';  
    return undefined;
}
var a = new fn;  
console.log(a); 
//fn {user: "李鋼鐵"}

    function fn()  
{  
    this.user = '李鋼鐵';  
    return 1;
}
var a = new fn;  
console.log(a.user); 
//李鋼鐵

特殊狀況:雖然null也是對象,可是在這裏this仍是指向那個函數的實例,由於null比較特殊。
function fn()  
{  
    this.user = '李鋼鐵';  
    return null;
}
var a = new fn;  
console.log(a.user); //李鋼鐵
相關文章
相關標籤/搜索