this 相關
對於前端程式媛(員)來說,this這個機制應用的地方是很多的,所以搞懂是必要的,不熟練使用this將遇到一些困惑,下面是一些關於this的學習心得分享,希望大家可以一起學習:
1,this並不是指向自身
首先來看一段程式碼
1function foo(num) { 2console.log("foo:" + num); //0 1 2 3 4 3console.log(this); //window物件 4this.count++; //記錄foo被呼叫的次數 5} 6foo.count = 0; //count 屬於foo的屬性並不屬於window 7var i; 8for (i = 0; i < 10; i++) { 9if (i < 5) { 10foo(i); 11} 12} 13console.log(foo.count) //0
很多人認為 :函式內部的this指向當前這個函式,這是錯誤的
對於這個例子來說,一種解決方法是使用foo識別符號代替this來指向自身
1function foo(num) { 2console.log("foo:" + num); //0 1 2 3 4 3console.log(this); //window物件 4foo.count++; //記錄foo被呼叫的次數 5} 6foo.count = 0; //count 屬於foo的屬性並不屬於window 7var i; 8for (i = 0; i < 10; i++) { 9if (i < 5) { 10foo(i); 11} 12} 13console.log(foo.count) //5
另一種方法是沒有規避了this使用
function foo(num) { console.log("foo:" + num); //0 1 2 3 4 console.log(this); //window物件 this.count++; //記錄foo被呼叫的次數 } foo.count = 0; //count 屬於foo的屬性並不屬於window var i; for (i = 0; i < 10; i++) { if (i < 5) { foo.call(foo, i) //使用call() 確保this 指向函式foo } } console.log(foo.count) //5
2,this指向函式作用域(並不是一直都是對的)
需要知道的一點是this在任何情況都不會指向函式的詞法作用域,因為作用域是無法通過js程式碼訪問到的,它存在於引擎內部
下面這個程式碼很好的說明了這個問題
function foo() { var a = 2; this.bar(); // this是window } function bar() { console.log(this); //window console.log(this.a); //undefined } foo();
this究竟是指向什麼呢?this是在執行時進行繫結的,並不是在編寫時繫結的,它的上下文取決於函式呼叫時的各種條件,this繫結和函式宣告的位置沒有任何關係,只取決於函式的呼叫方式。
當函式呼叫時,會建立一個執行上下文,這個上下文會記錄函式在哪裡呼叫了,函式的呼叫方法,函式引數等資訊,this就是該上下文的一個屬性,會在函式執行的過程中用到。
3,函式在執行過程中this是如何繫結的
尋找函式的呼叫位置,其實有的很明顯,而某些程式設計模式會因此函式的真正的呼叫位置
分析呼叫棧
1function baz() { 2//當前的呼叫棧是baz 3//因此,當前的呼叫位置是全域性作用域 4console.log("baz"); 5bar(); //bar的呼叫位置 6} 7 8function bar() { 9//當前的呼叫棧是baz->bar 10//因此,當前的呼叫位置是baz 11console.log("bar"); 12foo(); //foo的呼叫位置 13} 14 15function foo() { 16//當前的呼叫棧是baz->bar->foo 17//因此,當前的呼叫位置是bar 18console.log("foo"); 19} 20baz(); //baz的呼叫位置
分析呼叫棧是,決定了this的繫結
4,this的繫結規則
①預設繫結:獨立函式呼叫 ,在下面的程式碼中,foo()是直接使用不帶任何修飾符的函式引用進行呼叫的,因此只能使用預設繫結
1function foo() { 2console.log(this.a); //this指向window 3} 4var a = 2; 5foo(); //2
但是在嚴格模式下,全域性物件將無法使用預設繫結,因此this會繫結到undefined
1function foo() { 2'use strict' 3console.log(this); //undefined 4console.log(this.a); //報錯 5} 6var a = 2; 7foo();
但是有個微妙的非常重要的情況,雖然this的邦定規則完全取決於呼叫位置,但是隻有foo執行在非嚴格模式下是預設繫結才能繫結到全域性物件,但在嚴格模式下,於foo的呼叫位置無關了
1function foo() { 2console.log(this); //window 3console.log(this.a); //2 4} 5var a = 2; 6 7(function() { 8'use strict' 9console.log(this);//undefined 10foo(); 11})()
②隱式繫結
1 function foo() { 2console.log(this); //obj 3console.log(this.a); //2 4} 5var obj = { 6a: 2, 7foo: foo 8} 9obj.foo();
當foo被呼叫的時候,它的落腳點確實指向obj物件,當函式引用有上下文物件時,隱式繫結規則會把函式呼叫中的this邦定到這個上下文物件中,因此,呼叫foo()時,this被繫結到obj,因此,this.a和obj.a是一樣的
③顯示繫結
1function foo() { 2console.log(this); //obj 3console.log(this.a); //2 4} 5var obj = { 6a: 2 7} 8foo.call(obj)
.call(...)和apply(...)方法實現顯示繫結
他們的第一個引數是一個物件,不是物件的會利用自己的包裝物件轉換成物件(new Number() ,new String() ,new Boolen()等),他們會把這個物件繫結到this,接著在呼叫函式時指定這個this,因為你可以直接指定this的繫結物件,因此我們稱為顯式繫結
④new繫結
1function foo(a) { 2this.a = a; 3} 4var bar = new foo(2); 5console.log(bar.a);//2
建構函式是使用new操作時會被呼叫的函式。他們並不屬於類,也不會例項化一個類,實際上,甚至不能說他們是一直特殊的函式型別,他們只是用new操作費呼叫的普通函式而已。
使用new來呼叫函式,會自動執行下面操作:
1,建立(或者構造一個)全新的物件
2,這個物件會被執行【原型】連線
3,這個新物件會被繫結到函式呼叫的this,
4,如果函式沒有返回其他物件,那麼new表示式中的函式呼叫會自動返回這個新物件
上面的程式碼,使用new呼叫foo()時,我們會構造一個新的物件並把它繫結到foo()呼叫中的this上,new是最後一個可以影響函式呼叫時this邦定行為的方法,我們稱之為new繫結