使人心痛的血淋淋教訓,再犯這些錯誤我不是人。瀏覽器
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);
輸出結果:
this
緣由: 一、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 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);
函數提高在變量提高上面,第一個console.log(foo);爲何會輸出函數題呢,緣由在於 var foo; 並未有賦值只是聲明,所以他會調用上面的值.
全局環境中,this指向window console.log(this.document === document); // true // 在瀏覽器中,全局對象爲 window 對象: console.log(this === window); // true this.a = 37; console.log(window.a); // 37
函數調用
非嚴格模式下,this 默認指向全局對象window
function f1(){ return this f1() === window; // true 而嚴格模式下, this爲undefined function f2(){ "use strict"; // 這裏是嚴格模式 return this; } f2() === undefined; // true
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); //李鋼鐵