前段時間,看了《你不知道的JavaScript》中this的全面解析,講的特別好,還沒看過的小夥伴抓緊去學習,在這邊特地整理總結了一波,一塊兒分享學習。在這以前能夠先理解一下關於this、call、applay和bind這篇文章。git
在瞭解this
綁定原理以前,首頁要先了解JavaScript
的執行棧。(執行棧也叫作調用棧)若是對調用棧還不瞭解,能夠先詳細瞭解深刻淺出JavaScript執行上下文和執行棧。github
JavaScript
是單線程的,全部這決定了同一時間只能作一件事情,其餘的活動或事情只能排隊等候了,因而就生成出一個等候隊列的執行棧(Execution Stack)。簡單來講,執行棧就是爲了到達當前執行位置所調用的全部函數。而調用位置就在當前正在執行的函數的前一個調用中。app
function baz() { // 當前執行棧是:baz // 調用位置是baz前一個調用中,所以是全局做用域 console.log('baz'); bar(); } function bar() { // 當前執行棧是:bar // 調用位置是bar前一個調用中,所以是baz onsole.log('bar'); } baz() // baz的調用位置
根據上面的規則,則不難判斷出上述代碼的調用棧和調用位置。而調用棧和調用位置則決定了this
的綁定對象。其綁定規則有四種,只有找到調用位置,才能判斷須要應用四條規則中的哪一條。函數
在非嚴格模式下,this
指向全局對象,嚴格模式下this
則會指向undefined
post
function foo() { console.log(this.a); // this指向全局對象 } var a = 2; foo(); // 2 function foo2() { "use strict"; // 嚴格模式this綁定到undefined console.log(this.a); } foo2(); // TypeError:a undefined
可是若是在嚴格模式下調用其餘函數,則不影響默認綁定。學習
function foo() { console.log(this.a); // foo函數不是嚴格模式 默認綁定全局對象 } var a = 2; function foo2(){ "use strict"; foo(); // 嚴格模式下調用其餘函數,不影響默認綁定 } foo2();
上述爲獨立的函數調用,其調位位置爲全局做用域,全部this
綁定在全局做用域上。this
函數在調用位置,是否有上下文對象,若是有,那麼this
就會隱式綁定到這個對象上。
也能夠簡單理解爲是否被某個對象包含了。線程
function foo() { console.log(this.a); } var a = "Oops, global"; let obj2 = { a: 2, foo: foo }; let obj1 = { a: 22, obj2: obj2 }; obj2.foo(); // 2 this指向調用函數的對象 obj1.obj2.foo(); // 2 this指向最後一層調用函數的對象 // 隱式綁定丟失 let bar = obj2.foo; // bar只是一個函數別名 是obj2.foo的一個引用 bar(); // "Oops, global" - 指向全局
上述代碼中函數foo
調用位置在obj2
上下文中,全部this
綁定在obj2
做用域中,因此obj2.foo()
、obj1.obj2.foo()
最終都爲2,而let bar = obj2.foo
實際上就是函數的引用賦給變量bar
,調用時,並無上下文對象,因此會致使隱式綁定丟失。3d
經過apply
、call
、bind
將函數中的this
強制綁定到指定對象上。code
function foo() { console.log(this.a); } let obj = { a: 2 }; foo.call(obj); // 2
須要注意的是:
this
的綁定對象,這個原始值會轉換成它的對象形式。null
或者undefined
做爲this
的綁定對象傳入call
、apply
、bind
,這些值會在調用時被忽略,實際應用的是默認綁定規則。
若是對
new
關鍵字不太瞭解,能夠先看這篇
關於new命令。
使用構造調用的時候,this會自動綁定在new期間建立的對象上。
function foo(a) { this.a = a; // this綁定到bar上 } let bar = new foo(2); console.log(bar.a); // 2
function foo() { this.a = 100; } var obj1 ={ foo: foo; } var obj2 = {} obj1.foo.call(obj2, 2); // 2 this指向obj2 顯式綁定比隱式綁定優先級高。 new obj1.foo(4); // thsi指向new新建立的對象 new綁定比隱式綁定優先級高。
顯式綁定和new綁定沒法直接比較(會報錯),默認綁定是不該用其餘規則以後的兜底綁定因此優先級最低。
箭頭函數this不會使用這四條綁定規則。
function foo() { return (a) => { // this繼承自foo console.log(this.a); }; } let obj1 = { a: 2 }; let obj2 = { a: 3 }; let bar = foo.call(obj1); // foo this指向obj1 bar.call(obj2); // 輸出2 這裏執行箭頭函數 並試圖綁定this指向到obj2
從上述能夠得出,箭頭函數的this
規則:
this
繼承於它外面第一個不是箭頭函數的函數的this
指向。若是沒有則指向全局。this
一旦綁定了上下文,就不會被任何代碼改變。若是要判斷一個運行中函數this
綁定,就須要找到這個函數直接調用位置。找到以後就能夠順序應用下面四條規則來判讀this
綁定對象。
new
調用,綁定到新建立對象。call
或者apply
、bind
調用,綁定到指定對象。undefined
,不然綁定到全局箭頭並不適用與上述四條規則,而是由他的外層函數繼承而來。
更多優質文章能夠訪問GitHub博客,歡迎帥哥美女前來Star!!!