JS中的this詳解


簡介

在絕大數情況下,函數的調用方式決定了this值。this不能在執行期間被復制,並且在每次函數被調用時this的值也可能會不同。

this的值表示當前執行的環境對象,而與聲明環境無關,所以this代表的對象要等函數運行。類似定義函數時的參數列表,只有在函數調用時才傳入真正的對象。

this關鍵字雖然會根據環境變化,但它始終代表的是調用當前函數的對象。

全局環境

無論是否在嚴格模式下,在全局執行環境中(在任何函數體外部),this都指向全局對象。

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

函數環境中的this

在函數內部,this的值取決於函數被調用的方式。

簡單調用

在非嚴格模式下,this的值不是由該調用設置的,所以this的值默認指向全局對象。

function f1() {
    return this;
}

f1() === window;

在嚴格模式下,this將保持他進入執行環境時的值,所以this將會默認為undefined

function f2() {
    "use strict";
    return this;
}

f2() === undefined;

嚴格模式下,如果this沒有執行環境定義,那它將保持為undefined

如果想把this的值從一個環境傳到另一個,就要用call或者apply方法。

//將一個對象作為call和apply的第一個參數,this就會被綁定到這個對象。
var obj = {a: 'Custom '};

var a = "Global";

function whatsThis(arg) {
    return this.a;
}

whatsThis();			//'Global'
whatsThis.call(obj);	//'Custom'
whatsThis.apply(obj);	//'Custom'

當一個函數在其主體中使用this關鍵字時,可以使用函數繼承自Function.prototypecallapply方法將this值綁定到調用中的特定對象。

使用callapply函數的時候,如果傳遞給this的值不是一個對象,JavaScript會嘗試使用內部toObject操作將其轉換為對象。

function bar() {
  console.log(Object.prototype.toString.call(this));
}

bar.call(7); //[object Number]

bind方法

ES5引入了Function.prototype.bind。調用f.bind(someObject)會創建一個與f具有相同函數體和作用域的函數,但是在這個新函數中,this將永久的被綁定到了bind的第一個參數,無論這個函數是如何被調用的。

function f() {
    return this.a;
}

var g = f.bind({a:"azerty"});
console.log(g());	//azerty

var h = g.bind({a:"yoo"});	//bind只生效一次,所以此處bind無效
console.log(h());	//azerty

var o = {a:37, f:f, g:g, h:h};
console.log(o.f(), o.g(), o.h());	//37, azerty, azerty

箭頭函數

在箭頭函數中,this與普通函數的指向不同,它與封閉詞法環境的this保持一致。在箭頭函數中,this指向創建時所在的環境,這同樣適用於在其他函數內創建的箭頭函數;在普通函數中,this指向執行時的環境。

箭頭函數被調用的時候,不會自動綁定一個this對象。箭頭對象根本沒有自己的this,它的this都是捕獲自其所在上下文的this值。

舉栗子:

var foo = (( => this));
console.log(foo() === window);	//true

//作為對象的一個方法調用
var obj = {foo: foo};
console.log(obj.foo() === window);	//ture

//使用call來設定this
console.log(foo.call(obj) === window);	//true

//使用bind設定this
foo = foo.bind(obj);
console.log(foo() === window);	//true

第二個栗子:

//bar返回一個函數,這個函數返回this
//這個返回的函數是以箭頭函數創建的
//所以它的this被綁定到了外層函數的this。
var obj = {
  bar: function() {
    var x = (() => this);
    return x;
  }
};

//作為obj對象的一個方法來調用bar,把它的this綁定到obj。將返回的函數的引用復制給fn
var fn = obj.bar();
//直接調用fn而不設置this
//通常不適用箭頭函數的情況,默認為全局對象
//若在嚴格模式則為undefined
console.log(fn() === obj);	//true

//如果只是引用obj的方法,而沒有調用它
var fn2 = obj.bar;		//fn2 = function() {var x = (() => this); return x;}
//調用箭頭函數后,this指向window,因為它從bar繼承了this
console.log(fn2() === window);	/true

作為對象的方法

當函數作為對象里的方法被調用時,它們的this是調用該函數的對象。

栗子:

var o = {
  prop: 37
  f: function() {
      return this.prop;
  }
}

console.log(o.f());	//logs 37

函數時從of成員調用才是重點。

同樣,this的綁定只受最靠近的成員應用的影響。

o.b = {g: independent, prop: 42}
console.log(o.b.g())	//42

這個栗子說明,這與它是對象o的成員沒有什么關系,最靠近的引用才是最重要的。

原型鏈中的this

對於在對象原型鏈上某處定義的方法,同樣的概念也使用。如果該方法存在於一個對象的原型鏈上,那么this指向的是調用這個方法的對象,就像是該方法在對象上一樣。

var o = {
  f: function() {
    return this.a + this.b;
  }
};

var p = Object.create(o);
p.a = 1;
p.b = 4;

console.log(p.f());	//5

在這個栗子中,對象p沒有屬於自己的屬性,它的f屬性繼承自它的原型。雖然在對於f的查找過程中,最終是在o中找到的f屬性,但是查找股從橫首先從p.f的引用開始,所以函數中的this指向p。也就是說f是作為p的方法調用的,所以它的this指向了p。這是JavaScript的原型繼承中的一個有趣的特征。

getter與setter中的this

同樣的概念適用於,當函數在一個getter或者setter中被調用。用作gettersetter的函數都會把this綁定到設置或獲取屬性的對象。

function sum() {
  return this.a + this.b + this.c;
}

var o = {
  a: 1,
  b: 2,
  c: 3,
  get average() {
    return (this.a + this.b + this.c) / 3;
  }
};

Object.defineProperty(o, 'sum', {
    get: sum, enumerable: true, configurable: trye
})

console.log(o.average, o.sum);	//logs 2, 6

作為構造函數

當一個函數用作構造函數時(使用new關鍵字),它的this被綁定到正在構造的新對象。

雖然構造器返回的默認值是this所指的對象,但是也可以手動地返回其他的對象(如果返回值不是一個對象,則返回this對象)。

function C() {
    this.a = 37;
}

var o = new C();
console.log(o.a);	//logs 37

function C2() {
    this.a = 37;
    return {a:38};
}

o = new C2();
console.log(o.a);	//logs 38

在這個栗子中,在調用構造函數的過程中,手動的設置了返回對象,所以默認對象就被丟棄了。this.a = 37;這條語句雖然執行了,但是對於外部沒有任何影響。

作為一個DOM事件處理函數

當函數被用作事件處理函數時,它的this指向觸發事件的元素。

作為一個內聯事件處理函數

當代碼被內聯on-event處理函數調用時,它的this指向監聽器所在的DOM元素:

<button onclick="alert(this.tagName.toLowerCase());">
  Show this
</button>

這個栗子的alert會顯示button。只有外層代碼中的this是這樣設置的:

<button onclick="alert((function() {return this})());">
  Show inner this
</button>

在這種情況下,沒有設置內部函數的this,所以它指向window/global對象,非嚴格模式下,調用的函數未設置this時指向的默認對象。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM