首先,很多人對this有兩個誤解
1.this指向的是當前函式本身
什么意思呢,下面看一段代碼,
1 function func(){ 2 this.a ++; 3 } 4 5 var a = 0; 6 func.a = 0; 7 8 for(let i = 0; i < 5; i++){ 9 func(); 10 } 11 12 console.log(a); //->5 13 console.log(func.a);//->0
發現問題了,因為函式本身也是一個物件,如果this指向的是當前函式本身,那么在呼叫func函式5次后,func.a應該輸出的是5,但是程式結果輸出的是0,但是全域定義的a變數輸出了結果5,顯然在這里函式中this指向的不是函式本身,而指向了全域物件.
這是為什么呢?接著往下看,
2.this指向的是當前函式的作用域
先看一段代碼:
function foo(){ var a = 0; this.bar(); } function bar(){ console.log(this.a); }
foo();//->undefined
這段代碼在按照“this指向的是當前函式的作用域”這樣的理解下,在理想的執行狀態下是這樣子的:
//foo(); function foo(){ var a = 0; console.log(a); }
因為按照上文理解,this指向的是當前的函式作用域,所以這段代碼能夠執行成功是有問題的,如果this指向的是當前的函式作用域,foo函式的詞法作用域中并沒有一個識別符號為bar的函式;
在嚴格模式下,這段代碼是無法執行的(報錯),

this到底是一個什么樣的機制呢?那么這里下一個定義:this的指向是取決于函式呼叫的方式,跟函式怎么寫沒有半毛錢關系,this的指向并沒有在撰寫代碼的時候就系結了,而發生在函式的呼叫時,
當一個函式被呼叫時,會創建一個活動記錄(執行背景關系),這個記錄會包含函式在哪里被呼叫(呼叫堆疊)、函式的呼叫方式、傳入的引數等資訊,this就是這個記錄的一個屬性,會在函式執行的程序中用到,

函式呼叫位置的追蹤:
function baz(){ //當前的呼叫堆疊是:baz //因此,當前呼叫位置是全域作用域 console.log("baz"); bar(); // <-- bar的呼叫位置 } function bar(){ //當前呼叫堆疊是:baz -> bar //因此,當前呼叫位置在baz中 console.log("bar"); foo(); // <-- foo的呼叫位置 } function foo(){ //當前的呼叫堆疊是:baz -> bar -> foo //因此,當前的呼叫位置在bar中 console.log("foo"); } baz(); //<-- baz的呼叫位置
既然this的系結取決于函式的呼叫方式,那么this存在下面這四種系結規則:
- 默認系結
- 隱式系結
- 顯示系結
- new系結
默認系結:簡單地來講,默認系結就是單獨的,沒有任何修飾符的函式呼叫,就像這樣,
foo();
這時,this指向全域物件,下面給這個函式加上this來試一試,
function foo(){ console.log(this.num); } var num = 6; foo(); //->6
但是,只有在非嚴格模式下,this在默認系結下才能系結到全域物件上;如果在嚴格模式下,this會系結到undefined,
記住了,就光寫一個光禿禿的函式呼叫,就是默認系結,這時this指向的是全域物件(在非嚴格模式下),
隱式系結:又是簡單地來講,隱式系結就是當這個函式作為某個物件的方法被呼叫時,this就會指向這個物件,
obj.foo();
考慮一下呼叫的位置是否有背景關系物件,就是說這個函式是否被某個物件擁有或包含,
function foo(){ console.log(this.a); } var obj = { a : 2, foo : foo }; obj.foo(); // -> 2 這時foo函式的this指向obj這個物件
因為這時this函式指向的就是包含它的obj物件,所以此時this.a === obj.a
這就叫做隱式系結,不過隱式系結有一個問題,最常見的問題就是被隱式系結的函式會丟失系結物件,怎么回事呢,來看一下下面的代碼:
function foo(){ console.log( this.a ); } var obj = { a : 2, foo : foo }; var bar = obj.foo; var a = "global"; //a是全域物件的屬性 bar(); // -> "global"
看到沒?本來foo應該指向obj來著,現在指向了全域物件,是怎么回事呢?那是因為obj的方法foo作為一個單獨的函式被賦值給了bar,bar就成了foo函式的別名,輸出一下bar,看看里面是什么:
這里可以看到,bar的內容是foo函式的函式體,所以這里呼叫bar,與foo()陳述句等價,使用了默認系結,this指向全域物件,所以才會輸出"global",在編程中,要注意這個細節,
顯式系結:顯式系結說白了就是函式使用call方法或apply顯式地指定了this的指向,
例如:
function foo(){ console.log( this.a ); } var obj = { a : 2 }; foo.call( obj ); //->2
說到了call和apply,他倆也沒啥區別,只是呼叫時寫法有點不一樣:
func.call(this, arg1, arg2);
func.apply(this, [arg1, arg2]);
call的函式引數一直寫,中間用逗號隔開,apply的函式引數以陣列形式給出,
new系結:這個就不用說了,在用new呼叫函式時,函式內的this指向當前物件,
在JavaScript中,沒有明顯的識別符號什么的說明建構式是建構式,實際上,Javascript不存在建構式,只有被“構造呼叫”的普通函式,new關鍵字將一個普通函式“構造呼叫”,并且將這個普通函式的this與創建的物件相關聯,
function Person( name, sex ){ this.name = name; this.sex = sex; } // -> Person的this現在指向xiaoming var xiaoming = new Person("小明","男");
那么基本的this指向問題就這么簡單地說完了,總結一下,this的指向其實跟函式如何撰寫沒有什么關系,只跟函式呼叫的方式有關系,希望這篇文章可以幫到大家更好地理解this的問題,如果有補充或者疑問,請在評論區留言,謝謝!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/184946.html
標籤:其他
上一篇:HBuilderX配置外部服務器(tomcat)查看編輯jsp界面
下一篇:notes連接SAP問題
