this 其實是在函數被調用時發生的綁定,它指向什麼徹底取決於函數在哪裏被調用(也就是函數的調用方法)。數組
獨立函數調用時,應用 this
的默認綁定, this
指向全局對象
若是使用嚴格模式(strict mode),那麼全局對象將沒法使用默認綁定,所以 this
會綁定到 undefined
觀察下面這段代碼瀏覽器
function aba () { console.log(this) } function bab () { "use strict" console.log(this) } var obj = { aba: aba, bab: bab } aba() //Window {frames: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …} bab() //undefined obj.aba() //{aba: ƒ, bab: ƒ} obj.bab() //{aba: ƒ, bab: ƒ}
在一個對象內部包含一個指向函數的屬性,並經過這個屬性間接引用函數,從而把 this 間接(隱式)綁定到這個對象上。
當函數引用有上下文對象時,隱式綁定規則會把函數調用中的 this
綁定到這個上下文對象。
因此上個示例代碼中, 調用 obj.aba()
時 this
被綁定到 obj
,所以 this.aba
和 obj.aba
是同樣的。app
對象屬性引用鏈中只有
最頂層或者說最後一層會影響調用位置。函數
function foo() { console.log( this.a ); } var obj2 = { a: 42, foo: foo }; var obj1 = { a: 2, obj2: obj2 }; obj1.obj2.foo(); // 42
一個最多見的 this 綁定問題就是被隱式綁定的函數會丟失綁定對象,也就是說它會應用默認綁定,從而把 this 綁定到全局對象或者 undefined 上,取決因而否是嚴格模式。post
function fun1 () { console.log(this) } var obj = { fun1: fun1, fun2: function () { console.log(this) } } var getfun1 = obj.fun1 var getfun2 = obj.fun2 function doCallBack (cb) { //參數傳遞其實就是一種隱式賦值,所以咱們傳入函數時也會被隱式賦值 cb() } function testSetTimeout (fun) { setTimeout(fun, 0) } fun1() // Window {frames: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …} obj.fun1() // {fun1: ƒ, fun2: ƒ} obj.fun2() // {fun1: ƒ, fun2: ƒ} getfun1() // Window {frames: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …} getfun2() // Window {frames: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …} doCallBack(obj.fun1) // Window {frames: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …} doCallBack(obj.fun2) // Window {frames: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …} testSetTimeout(obj.fun1) // Window {frames: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …} testSetTimeout(obj.fun2) // Window {frames: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …}
使用 call
、 apply
、 bind
或者箭頭函數顯示綁定函數調用的 this
若是把 null
或者 undefined
做爲 this
的綁定對象傳入 call
、apply
或者 bind
,這些值在調用時會被忽略,實際應用的是默認綁定規則this
var obj = { c: 33, fun: function (a,b) { console.log( "a:" + a + ", b:" + b + ", c:" + this.c ) } } // 把數組「展開」成參數 obj.fun.apply( null, [2, 3] ); a:2, b:3, c:undefined // 使用 bind(..) 進行柯里化 var bar = obj.fun.bind( null, 2 ); bar( 3 ); // a:2, b:3, c:undefined
上面代碼中 apply
、 bind
這兩種方法都須要傳入一個參數看成 this 的綁定對象。若是函數並不關心 this
的話,仍然須要傳入一個佔位值,這時 null 多是一個不錯的選擇。
可是若是某個函數確實使用了 this
,那默認綁定規則會把 this
綁定到全局對象(在瀏覽器中這個對象是 window
),這將致使不可預計的後果(好比修改全局對象)。code
在 JavaScript 中,構造函數只是一些被 new 操做符調用的普通函數而已。
實際上並不存在所謂的「構造函數」,只有對於函數的「構造調用」。對象使用 new 來調用函數,或者說發生構造函數調用時,會自動執行下面的操做。
1 建立(或者說構造)一個全新的對象。
2 這個新對象會被執行 [[ 原型 ]] 鏈接。
3 這個新對象會綁定到函數調用的 this。
4 若是函數沒有返回其餘對象,那麼 new 表達式中的函數調用會自動返回這個新對象。ip
注意: 對 bind
返回的函數進行構造調用時,實際調用的是原函數,調用 bind
時傳入的第一個參數將被忽略,剩餘參數做爲構造調用的參數傳入原函數get
function test (a, b) { this.a = a this.b = b this.say = function () { console.log(`a: ${this.a}, b: ${this.b}, c: ${this.c}`) } } var bindObj = { c: 12 } var bindReturn = test.bind(bindObj, 1, 2) var newBindReturn = new bindReturn() newBindReturn.say() // a: 1, b: 2, c: undefined console.log(bindObj) // {c: 12} bindReturn() console.log(bindObj) // {c: 12, a: 1, b: 2, say: ƒ}