今天來給大家分析js中this的指向幾種情況?
之前在寫程式碼時遇到this時,總是在考慮應不應該用,原因是當時分不清楚,後來把this的所有情況分析了一遍,其實超級簡單,而且他的使用場景很好,下面我來把他的幾種情況分析一下,如果this分佈太清得小夥伴們可以參考哈~希望對你們有幫助!
與其他語言相比,函式this在 JavaScript 中的表現略有不同,此外,在嚴格模式和非嚴格模式之間也會有一些差別.在絕大多數情況下,函式的呼叫方式決定了this
的值。this
不能在執行期間被賦值,並且在每次函式被呼叫時this
的值也可能會不同。ES5引入了bind方法來設定函式的this
值,而不用考慮函式如何被呼叫的,ES2015 引入了支援this
this
的值)。(1)無論是否在嚴格模式下,在全域性執行上下文中(在任何函式體外部)this
都指代全域性物件。
// 在瀏覽器中, window 物件同時也是全域性物件:
console.log(this === window); // true
a = 37;
console.log(window.a); // 37
(2)在函式內部,this
的值取決於函式被呼叫的方式
function f1(){
return this;
}
//在瀏覽器中:
f1() === window; //在瀏覽器中,全域性物件是window
//在Node中:
f1() === global;
然而,在嚴格模式下,this
將保持他進入執行上下文時的值,所以下面的this
將會預設為undefined
。
function f2(){
"use strict"; // 這裡是嚴格模式
return this;
}
f2() === undefined; // true
如果要想把 this
的值從一個上下文傳到另一個,就要用 call
或者apply
方法。
// 將一個物件作為call和apply的第一個引數,this會被繫結到這個物件。
var obj = {a: 'Custom'};
// 這個屬性是在global物件定義的。
var a = 'Global';
function whatsThis (arg) {
return this.a; // this的值取決於函式的呼叫方式
}
whatsThis(); // 'Global'
whatsThis.call(obj); // 'Custom'
whatsThis.apply(obj); // 'Custom'
當一個函式在其主體中使用 this
關鍵字時,可以通過使用函式繼承自Function.prototype
的 call
或 apply
方法將 this
值繫結到呼叫中的特定物件。
function add(c, d) {
return this.a + this.b + c + d;
}
var o = {a: 1, b: 3};
// 第一個引數是作為‘this’使用的物件
// 後續引數作為引數傳遞給函式呼叫
add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16
// 第一個引數也是作為‘this’使用的物件
// 第二個引數是一個數組,數組裡的元素用作函式呼叫中的引數
add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34
使用 call
和 apply
函式的時候要注意,如果傳遞給 this
的值不是一個物件,JavaScript 會嘗試使用內部 ToObject
操作將其轉換為物件。因此,如果傳遞的值是一個原始值比如 7
或 'foo'
,那麼就會使用相關建構函式將它轉換為物件,所以原始值 7
會被轉換為物件,像 new Number(7)
這樣,而字串 'foo'
轉化成 new String('foo')
這樣,例如:function bar() {
console.log(Object.prototype.toString.call(this));
}
//原始值 7 被隱式轉換為物件
bar.call(7); // [object Number]
(3)ECMAScript 5 引入了 。呼叫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只生效一次!
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
(4)箭頭函式在箭頭函式中,this
與封閉詞法上下文的this
保持一致。在全域性程式碼中,它將被設定為全域性物件:
var globalObject = this;
var foo = (() => this);
console.log(foo() === globalObject); // true
// 接著上面的程式碼
// 作為物件的一個方法呼叫
var obj = {foo: foo};
console.log(obj.foo() === globalObject); // true
// 嘗試使用call來設定this
console.log(foo.call(obj) === globalObject); // true
// 嘗試使用bind來設定this
foo = foo.bind(obj);
console.log(foo() === globalObject); // true
注意:如果將this
傳遞給call
、bind
、或者apply
,它將被忽略。不過你仍然可以為呼叫新增引數,不過第一個引數(thisArg
)應該設定為null
。
無論如何,foo
的 this
被設定為他被建立時的上下文(在上面的例子中,就是全域性物件)。這同樣適用於在其他函式內建立的箭頭函式:這些箭頭函式的this
被設定為封閉的詞法上下文的。
(5)當函式作為物件裡的方法被呼叫時,它們的 this
是呼叫該函式的物件。下面的例子中,當 o.f()
被呼叫時,函式內的this
將繫結到o
物件。
var o = {
prop: 37,
f: function() {
return this.prop;
}
};
console.log(o.f()); // logs 37
(6)對於在物件原型鏈上某處定義的方法,同樣的概念也適用。如果該方法存在於一個物件的原型鏈上,那麼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屬性繼承自它的原型。雖然在對 f
的查詢過程中,最終是在 o
中找到 f
屬性的,這並沒有關係;查詢過程首先從 p.f
的引用開始,所以函式中的 this
指向p
。也就是說,因為f
是作為p
的方法呼叫的,所以它的this
指向了p
。這是 JavaScript 的原型繼承中的一個有趣的特性。
(7)再次,相同的概念也適用於當函式在一個 getter
或者 setter
中被呼叫。用作 getter
或 setter
的函式都會把 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: true});
console.log(o.average, o.sum); // logs 2, 6
(8)當一個函式用作建構函式時(使用new關鍵字),它的this
被繫結到正在構造的新物件。
雖然構造器返回的預設值是this
所指的那個物件,但它仍可以手動返回其他的物件(如果返回值不是一個物件,則返回this
物件)。
/*
* 建構函式這樣工作:
*
* function MyConstructor(){
* // 函式實體寫在這裡
* // 根據需要在this上建立屬性,然後賦值給它們,比如:
* this.fum = "nom";
* // 等等...
*
* // 如果函式具有返回物件的return語句,
* // 則該物件將是 new 表示式的結果。
* // 否則,表示式的結果是當前繫結到 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
(9)當函式被用作事件處理函式時,它的this
指向觸發事件的元素(一些瀏覽器在使用非addEventListener
的函式動態新增監聽函式時不遵守這個約定)。
// 被呼叫時,將關聯的元素變成藍色
function bluify(e){
console.log(this === e.currentTarget); // 總是 true
// 當 currentTarget 和 target 是同一個物件時為 true
console.log(this === e.target);
this.style.backgroundColor = '#A5D9F3';
}
// 獲取文件中的所有元素的列表
var elements = document.getElementsByTagName('*');
// 將bluify作為元素的點選監聽函式,當元素被點選時,就會變成藍色
for(var i=0 ; i<elements.length ; i++){
elements[i].addEventListener('click', bluify, false);
}
(10)當代碼被內聯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
,所以它指向 global/window 物件(即非嚴格模式下呼叫的函式未設定this
時指向的預設物件)。OK今天的內容就分享到這裡啦,