this指向
- 2020 年 7 月 28 日
- 筆記
- javascript
前言
首先需要明確的是,this的指向在函數定義的時候是無法確定的,只有函數執行的時候才能確定this到底指向誰。在非箭頭函數下,this指向調用其所在函數的對象,而且是離誰近就指向誰(此對於常規對象,原型鏈,getter&setter等都適用);構造函數下,this與被創建的新對象綁定;DOM事件中,this指向觸發事件的元素;還有內聯事件、延時函數,箭頭函數等情況。以下展開討論
全局環境下
在全局環境下,無論是否嚴格模式,this始終指向全局對象(window)
console.log(this.document === document); // true
// 在瀏覽器中,全局對象為 window 對象:
console.log(this === window); // true
this.a = 37;
console.log(window.a); // 37
函數上下文調用
函數直接調用
普通函數內部的this分【嚴格模式】和【非嚴格模式】兩種情況
非嚴格模式下,this默認指向全局對象window
function f1(){
return this;
}
f1() === window; // true
嚴格模式下,this默認為undefined
function f2(){
"use strict"; // 這裡是嚴格模式
return this;
}
f2() === undefined; // true
對象中的this
對象內部方法的this指向調用這些方法的對象。注意:
1.函數的定義無法確定this的指向,this指向只與調用函數的對象有關
2.多層嵌套的對象,內部方法的this指向離被調用函數最近的對象
var o = {
a:10,
b:{
a:12,
fn:function(){
console.log(this.a);//12
}
}
}
o.b.fn();
var o = {
a:10,
b:{
a:12,
fn:function(){
console.log(this.a); //undefined
console.log(this); //window
}
}
}
var j = o.b.fn;
j();
為什麼這裡的this會指向window呢,首先我們知道看this指向就看執行的時候調用它的對象。上例中雖然函數fn是被對象b引用,但是在將fn賦值給變數j的時候並沒有執行,而變數j是window的屬性,所以當後面調用j()的時候指向的是window。
原型鏈中的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,但當在執行p.f()時,會查找p的原型鏈,找到f函數並執行,這個時候是p調用f函數。
以上對於函數作為getter&setter時同樣適用。
構造函數中的this
構造函數中的this與新創建的新對象綁定。
function Fn(){
this.user = "jjj";
}
var a = new Fn();
console.log(a.user); //jjj
這裡對象a可以點出函數Fn裡面的user是因為new時用變數a創建了一個Fn的實例,將構造函數中的this與新創建的實例化對象a綁定,相當於複製了一份Fn到對象a裡面,此時僅僅是創建,並沒有執行,而執行a.user時調用Fn的對象是a,那麼this指向的自然就是a。
構造函數中有return時
function Fn(){
this.user = "jjj";
return { user: 'aaa' }
}
var a = new Fn();
console.log(a.user); //aaa
function fn()
{
this.user = 'jjj';
return {};
}
var a = new fn;
console.log(a.user); //undefined
function fn()
{
this.user = 'jjj';
return function(){};
}
var a = new fn;
console.log(a.user); //undefined
function fn()
{
this.user = 'jjj';
return 1;
}
var a = new fn;
console.log(a.user); //jjj
function fn()
{
this.user = 'jjj';
return undefined;
}
var a = new fn;
console.log(a.user); //jjj
function fn()
{
this.user = 'jjj';
return undefined;
}
var a = new fn;
console.log(a); //fn {user: "jjj"}
總結:如果返回值是一個對象,那麼this指向的就是返回的那個對象,如果返回值不是一個對象,那麼this還是指向函數的實例。
function fn()
{
this.user = 'jjj';
return null;
}
var a = new fn;
console.log(a.user); //jjj
值得注意的一點是,雖然null也是對象,但如果是返回了null,這個時候的this還是指向那個函數的實例,null比較特殊。
DOM事件處理函數
當函數被當做監聽事件處理函數時,其this指向觸發該事件的元素(針對於addEventListener事件)
// 被調用時,將關聯的元素變成藍色
function bluify(e){
console.log(this); //在控制台列印出所點擊元素
e.stopPropagation(); //阻止時間冒泡
e.preventDefault(); //阻止元素的默認事件
this.style.backgroundColor = '#A5D9F3';
}
// 獲取文檔中的所有元素的列表
var elements = document.getElementsByTagName('*');
// 將bluify作為元素的點擊監聽函數,當元素被點擊時,就會變成藍色
for(var i=0 ; i<elements.length ; i++){
elements[i].addEventListener('click', bluify, false);
}
內聯事件
內聯事件中的this指向分兩種情況:
- 當程式碼被內聯處理函數調用時,它指向監聽器所在的DOM元素
- 當程式碼被包括在函數內部執行時,其this指向等同於函數直接調用的情況,即非嚴格模式指向全局對象window,嚴格模式指向undefined
頁面
控制台列印
setTimeout & setInterval
對於延時函數內部的回調函數的this指向全局對象window
//默認情況下程式碼
function Person() {
this.age = 0;
setTimeout(function() {
console.log(this);// 列印出window對象
}, 3000);
}
var p = new Person();//3秒後返回 window 對象
當然也可以通過bind方法改變其內部函數的this指向
//通過bind綁定
function Person() {
this.age = 0;
setTimeout((function() {
console.log(this);// 列印出Person對象
}).bind(this), 3000);
}
var p = new Person();//3秒後返回構造函數新生成的對象 Person{...}
箭頭函數中的this
箭頭函數不綁定this,它會捕獲其所在上下文的this值,作為自己的this值。需要注意的是:
- call(),apply(),bind()方法對於箭頭函數來說只是傳入參數,對它的this毫無影響
- 考慮到this是詞法層面上的,可以忽略是否在嚴格模式下的影響。
function Person() {
this.age = 0;
setTimeout(() => {
console.log(this) // Person對象
}, 3000);
}
var p = new Person();
function Person() {
this.age = 0;
setTimeout(function() {
console.log(this) // Window對象
}, 3000);
}
var p = new Person();
驗證嚴格模式影響
var f = () => {'use strict'; return this};
var p = () => { return this};
console.log(1,f() === window);
console.log(2,f() === p());
//1 true
//2 true
以上的箭頭函數都是在方法內部,以非方法的方式調用,如果將箭頭函數當做方法調用會怎樣呢?
var obj = {
i: 10,
b: () => console.log(this.i, this),
c: function() {
console.log( this.i, this)
}
}
obj.b(); // undefined window{...}
obj.c(); // 10 Object {...}
可以看到,作為方法的箭頭函數指向全局window對象,而普通函數則指向調用它的對象。
參考:
//www.cnblogs.com/pssp/p/5216085.html
//www.cnblogs.com/dongcanliang/p/7054176.html