關于 JS 函數中的 this 指向

函數中的 this 如果不系統的深入的去學習,總會讓人感到困惑。它的字面意思往往給人一種就是指向函數自身的感覺,事實并非如此,反而這種感覺錯的很離譜。
無論怎樣,this 的指向不是函數聲明時確定的,而是在函數調用時確定的,它依賴于函數調用的上下文條件。
this 是什么?

this 的作用:

以一種優雅的方式“隱含”的傳遞函數執行環境對象的引用。
在對象和原型中,一組能自動引用恰當執行環境對象的函數將非常有用。

this 指向的四種規則:

一, 默認指向

function foo() { console.log(this.a) }
var a=1;
foo(); // 1

function bar() {
  var a = 2;
  return function bar() { console.log(this.a) }
}
bar()(); // 1

對于默認指向,如果是嚴格模式,this 指向 undefined;非嚴格模式,則是全局對象,如在瀏覽器中指向window.

二,隱含指向

function foo() { console.log(this.a) };
var a = 1;
var obj = { a:2, foo:foo };
obj.foo(); // 2
// 上例中,函數 foo() 調用時,前置了一個環境對象 obj,那么此時 foo 中的 this 就會指向它的環境對象 obj。

var obj2 = { a:3, obj: obj }
obj2.obj.foo(); // 2
// 如果時這種情況,則 this 指向引用鏈的最后一層,即obj

// this 也會隱含的“丟失”,如下面三種情況
// 第一種
var foo1 = obj.foo();
foo1(); // 1
// 牢記函數的this綁定是函數被調用時確定的,foo1 執行時,只是一個直白,無修飾的調用,此時符合默認指向規則。

//第二種
function go(fn) { fn() }
go(obj.foo) // 1
// go 傳入的實參 obj.foo 只是賦值給了形參 fn,本質上就是 fn = obj.foo,然后 fn() 。它和第一種是類似的。

// 第三種
setTimeout( obj.foo, 1000) // 1
// 本質上,這個this指向的過程,類似于第二種。

三,明朗指向

var obj = { a:1 }, a = 2;
function foo() { console.log(this.a) };
foo.call(obj); // 1 ; calll 和 apply 類似,不再贅述。

function foo1(c, d) { console.log(this.a, c, d) };
function go(fn, obj) { return function(){ return fn.apply(obj, arguments) }}     
setTimeout( function() { go(foo1, obj)(4, 5) }, 1000 ) // 1 4 5

// 當然我們可以使用 bind 實現另一種更強硬的明朗指向
setTimeout( foo1.bind(obj, 4, 5), 1000 ) // 1 4 5

四,new 指向

實際上,js中所謂的構造函數普通的函數別無二致。只有當函數前面加上 new 操作符被調用時的幾毫秒內(或許更短),它才是構造函數。所以,不存在構造器函數,只有函數的構造器調用.

當一個函數被前置的 new 操作符調用時,會發生以下四件事:
1,一個全新的對象(暫且叫它myObj吧)被憑空創建。
2,myObj 被接入被調用函數的原型鏈。
3,函數中的 this 指向 myObj
4,返回這個 myObj

function foo(a) { this.a = a };
var bar = new foo(1);
console.log(bar.a) // 1

五,箭頭函數的 this 指向
ES6 的箭頭函數不是通過 function 聲明的,而是通過大箭頭=> 聲明。箭頭函數從封閉它的作用域來確定this 指向,而且一旦指向確定,就無法被覆蓋。

function foo() { return () => { console.log(this.a) }
var obj1 = { a:1 }
var obj2 = { a:2 }
var bar = foo.call(obj1)

bar.call(obj2) // 1
this指向規則的優先順序

箭頭函數 > new > bind > apply=call > 隱含指向 > 默認指向

另外,當我們使用bind時,如果我們不關心this 的指向問題,而只想預設值幾個參數,你可能會傳遞一個null,更安全的方式是傳遞一個完全為空的對象,比如:Object.create(null).

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容