淺析Javascript 中的 this 的七種使用場景

Javascript 中的 this,有時候讓人迷惑,所以總結了一下關于this指向的問題。

在函數中 this 到底取何值,是在函數真正被調用執行的時候確定下來的,函數定義的時候確定不了。

因為 this 的取值是函數執行上下文(context)的一部分,每次調用函數,都會產生一個新的執行上下文環境。當代碼中使用了 this,這個 this 的值就直接從執行的上下文中獲取了,而不會從作用域鏈中搜尋。

關于 this 的取值,大體上可以分為以下七種情況:

情況一:全局 & 調用普通函數

在全局環境中,this 永遠指向 window。

console.log(this === window);? ? //true

普通函數在調用時候(注意不是構造函數,前面不加 new),其中的 this 也是指向 window。

var x = 10;

function foo(){

console.log(this);? ? //Window

console.log(this.x);? //10

}

foo();

情況二:構造函數

所謂的構造函數就是由一個函數 new 出來的對象,一般構造函數的函數名首字母大寫,例如像 Object,Function,Array 這些都屬于構造函數。

function Foo(){

this.x = 10;

console.log(this);? ? //Foo {x:10}

}

var foo = new Foo();

console.log(foo.x);? ? ? //10

上述代碼,如果函數作為構造函數使用,那么其中的 this 就代表它即將 new 出來的對象。

但是如果直接調用 Foo 函數,而不是 new Foo(),那就變成情況1,這時候 Foo() 就變成普通函數。

function Foo(){

this.x = 10;

console.log(this);? ? //Window

}

var foo = Foo();

console.log(foo.x);? ? ? //undefined

情況三:對象方法

如果函數作為對象的方法時,方法中的 this 指向該對象。

var obj = {

x: 10,

foo: function () {

console.log(this);? ? ? ? //Object

console.log(this.x);? ? ? //10

}

};

obj.foo();

注意:若是在對象方法中定義函數,那么情況就不同了。

var obj = {

x: 10,

foo: function () {

function f(){

console.log(this);? ? ? //Window

console.log(this.x);? ? //undefined

}

f();

}

}

obj.foo();

可以這么理解:函數 f 雖然是在 obj.foo 內部定義的,但它仍然屬于一個普通函數,this 仍指向 window。

在這里,如果想要調用上層作用域中的變量 obj.x,可以使用 self 緩存外部 this 變量。

var obj = {

x: 10,

foo: function () {

var self = this;

function f(){

console.log(self);? ? ? //{x: 10}

console.log(self.x);? ? //10

}

f();

}

}

obj.foo();

如果 foo 函數不作為對象方法被調用:

var obj = {

x: 10,

foo: function () {

console.log(this);? ? ? //Window

console.log(this.x);? ? //undefined

}

};

var fn = obj.foo;

fn();

obj.foo 被賦值給一個全局變量,并沒有作為 obj 的一個屬性被調用,那么此時 this 的值是 window。

情況四:構造函數 prototype 屬性

function Foo(){

this.x = 10;

}

Foo.prototype.getX = function () {

console.log(this);? ? ? ? //Foo {x: 10, getX: function}

console.log(this.x);? ? ? //10

}

var foo = new Foo();

foo.getX();

在 Foo.prototype.getX 函數中,this 指向的 foo 對象。不僅僅如此,即便是在整個原型鏈中,this 代表的也是當前對象的值。

情況五:函數用 call、apply或者 bind 調用。

var obj = {

x: 10

}

function foo(){

console.log(this);? ? //{x: 10}

console.log(this.x);? //10

}

foo.call(obj);

foo.apply(obj);

foo.bind(obj)();

當一個函數被 call、apply 或者 bind 調用時,this 的值就取傳入的對象的值。

情況六:DOM event this

在一個 HTML DOM 事件處理程序里,this 始終指向這個處理程序所綁定的 HTML DOM 節點:

function Listener(){

document.getElementById('foo').addEventListener('click', this.handleClick);? ? //這里的 this 指向 Listener 這個對象。不是強調的是這里的 this

}

Listener.prototype.handleClick = function (event) {

console.log(this);? ? //

}

var listener = new Listener();

document.getElementById('foo').click();

這個很好理解,就相當于是給函數傳參,使 handleClick 運行時上下文改變了,相當于下面這樣的代碼:

var obj = {

x: 10,

fn: function() {

console.log(this);? ? ? ? //Window

console.log(this.x);? ? ? //undefined

}

};

function foo(fn) {

fn();

}

foo(obj.fn);

你也可以用通過 bind 切換上下文:

function? Listener(){

document.getElementById('foo').addEventListener('click',this.handleClick.bind(this));

}

Listener.prototype.handleClick = function (event) {

console.log(this);? ? //Listener {}

}

var listener = new Listener();

document.getElementById('foo').click();

前六種情況其實可以總結為: this 指向調用該方法的對象。

情況七:箭頭函數中的 this

當使用箭頭函數的時候,情況就有所不同了:箭頭函數內部的 this 是詞法作用域,由上下文確定。

var obj = {

x: 10,

foo: function() {

var fn = () => {

return () => {

return () => {

console.log(this);? ? ? //Object {x: 10}

console.log(this.x);? ? //10

}

}

}

fn()()();

}

}

obj.foo();

現在,箭頭函數完全修復了 this 的指向,this 總是指向詞法作用域,也就是外層調用者 obj。

如果使用箭頭函數,以前的這種 hack 寫法:

var self = this;

就不再需要了。

var obj = {

x: 10,

foo: function() {

var fn = () => {

return () => {

return () => {

console.log(this);? ? // Object {x: 10}

console.log(this.x);? //10

}

}

}

fn.bind({x: 14})()()();

fn.call({x: 14})()();

}

}

obj.foo();

由于 this 在箭頭函數中已經按照詞法作用域綁定了,所以,用 call()或者 apply()調用箭頭函數時,無法對 this 進行綁定,即傳入的第一個參數被忽略。

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

推薦閱讀更多精彩內容

  • 工廠模式類似于現實生活中的工廠可以產生大量相似的商品,去做同樣的事情,實現同樣的效果;這時候需要使用工廠模式。簡單...
    舟漁行舟閱讀 7,827評論 2 17
  • 單例模式 適用場景:可能會在場景中使用到對象,但只有一個實例,加載時并不主動創建,需要時才創建 最常見的單例模式,...
    Obeing閱讀 2,097評論 1 10
  • 在面向對象的語言中(例如Java,C#等),this含義是明確且具體的,即指向當前對象。一般在編譯期綁定。而在ja...
    一木_qintb閱讀 407評論 0 0
  • 與其他語言相比,函數的this關鍵字在JavaScript中的表現略有不同,此外,在嚴格模式和非嚴格模式之間也會有...
    codingC閱讀 586評論 0 0
  • 你再說我胖,我就說你丁丁小。 壹 濱崎步又被掛熱搜了,繼濱崎步聾了,濱崎步結婚了,濱崎步離婚了后,濱崎步胖了光榮登...
    是年閱讀 442評論 0 2