Javascript千面之變幻莫測的this指向

Javascript千面之變幻莫測的this指向

相信很多前端人對“this”的指向是很懵逼的,因爲this的指向總是變幻莫測,在不同的調用環境中,它的指向總是各不相同。

在面試中,this也是經常考的必考題之一,很多前端老鳥經常會在this這裏掉坑。

接下來,看筆者來一層一層的揭開this指向的面紗。

1.事件調用環境中的this指向


function event() {

console.log(this)

}

// box1.onclick = event ------>


// box2.onclick = event ------>

// event() ------> window對象


從上述實例中可以看出:

box1點擊後,event方法中this指向div.class.box1

box2點擊後,event方法中this指向div.class.box2

event()直接調用,this指向window對象,嚴格模式下指向的是undefined

總結:1.當事件被動調用時,誰去觸發,this就指向誰; 2.當時間主動執行時(事件後面加()), this指向的就是window,嚴格模式下指向的是undefined

2.全局環境下的this指向
前端全局環境分兩部分,瀏覽器環境,node環境

瀏覽器全局環境

console.log(this)

}
// console.log(this) ------> window對象
// aaatestEvent() === window.aaatestEvent() ------> window對象
// aaa = window.aaa

我相信有很多小夥伴應該試過在script書寫的時候直接打印出this的指向,看看this指向的是個什麼東東?沒錯,指向的是window全局對象;我們經常定義的常量和變量也存放在window對象中

node環境
// console.log(this) -----> {}
// console.log(this === module.exports) -----> true
小夥伴從上述實例中可以看出,在node環境中,this指向的是暴露出來的對象

總結:全局環境中,瀏覽器環境指向的是window對象,node環境指向的是暴露出來的object對象

3.函數內部環境中的this指向
在實際開發中,調用函數方法時,爲了節約對內存的消耗,函數方法經常被存放於一個object對象中去,有利於方法的管理性和可讀性

實例1:

num: 100,
fn: function () {
  console.log(this)
}

}
// 調用
// obj.fn() ----> this指向obj

obj.fn()運行後,this指向的obj,可以發現,函數方法存在對象中的時候,函數內部的this指向的調用它的對象

實例2:

num: 100,
fn: function () {
  console.log(this)
}

}
// 調用
// obj.fn() ----> this指向obj
// window.obj.fn() ---> this指向obj

var obj1 = {
num: 100,
aaa: {

fn: function () {
  console.log(this)
}

}
}
// 調用
// window.obj1.aaa.fn() ---> this指向aaa
通過實例1中的測試,我們稍微總結了下this指向的是調用它的對象;

我們在上面瀏覽器全局環境中講過,定義常量和變量會被存放在window對象中,通過window對象輸出,我們也能發現obj在window中,可以嘗試去執行

window.obj.fn(),我們發現this還是指向obj啊?筆者剛剛不是說this指向的是調用它的對象嗎?現在是window在調用obj的fn方法啊?怎麼回事?

window.obj1.aaa.fn(),我們發現this指向了aaa啊?

解析:

實例1:obj.fn()    fn被obj調用,this指向obj

實例2:window.obj.fn()    fn被window調用,this指向obj

window.obj1.aaa.fn()    fn被window調用,this指向aaa

此時,我們總結:

實例1:【this指向調用它的對象】

實例2:【函數被多層對象包含,函數被最外層對象調用時,this指向的是它的上一級的對象】

有很多面試過程中,經常在這個地方會被問到,注意別掉坑哦?

有些考官特別可惡,喜歡在這個地方設置障礙故意繞懵面試者,請看下面的測試實例

num: 100,
aaa: {
  fn: function () {
    console.log(this)
  }
}

};

var abc = obj.aaa.fn;
// window.obj.aaa.fn() ------> 指向aaa
// abc() ------>指向window
// abc()等同於window.abc(),此處abc省略了window

解析上面實例

window.obj.aaa.fn()  就是上面總結的實例2,this指向的是它的上一級的對象

此時假如window.obj.aaa.fn被定義爲到了一個abc上,調用abc()就相當於調用window.abc(), 實例1的總結是這樣的:this指向調用它的對象,this的輸出執行是abc方法被執行了,誰調用了?沒錯window對象調用了,所以this指向的就是window對象,此處非常的繞,請小夥伴們仔細思考哦!

4.構造函數環境中的this指向
哈哈哈,凡事都是有例外的,當我們在寫構造函數或者在寫面向對象的邏輯中的時候,this的指向情況又不一樣了,請看實例:

不含返回值的構造函數

this.num = 10
console.log(this)

}

var obj = new fn();
console.log(obj)
/*

  • new在此時的作用
    *
    1. 調用fn這個函數方法
    1. 自動創建一個object對象
    1. 把創建出來的對象與this進行綁定
  • 4。如果構造函數沒有返回值,隱式返回this對象
  • */

上面的例子可以看出

this返回的東西和fn new出來的實例返回的東西是一樣的,這個就是new的魅力

哈哈哈,扯遠了,主要是照顧一下剛入前端坑的小夥伴們,解釋下new所做的事情

    1. 調用fn這個函數方法

    2. 自動創建一個object對象

    3. 把創建出來的對象與this進行綁定

    4. 如果構造函數沒有返回值,隱式返回this對象

總結:構造函數中,this指向的是新創建出來的並且如果又屬性值,進行綁定屬性的新建對象

ok, ok,anyway!我們再來個複雜的,同時也是更有意思的實例

this.num = 10
console.log(this)

}

// 此處的num與fn中的num不是同一個東西哦
fn.num = 20;
// 每個方法都有自己的原型鏈,在原型鏈上定義一個num
fn.prototype.num = 30;
// 在原型鏈上定義一個method的方法
fn.prototype.method = function () {

console.log(this);

}

var prototype = fn.prototype; 就相當於var prototype = {}
var method = prototype.method

// 直接new fn
new fn()
new fn().method()
prototype.method()
method()

實例解析:

1.new fn()  剛剛說了,this指向的是新創建出來的並且如果又屬性值,進行綁定屬性的新建對象 ,所以,此時this輸出的值是{num: 10}

2.new fn().method(), new的作用說了,先調用方法,然後創建一個對象,然後進行綁定this,最後輸出;
    new fn().method()就相當於{}.method(), 空對象{}綁定this下面的num = 10,所以此時this輸出的值是{num: 10}

3.函數方法都有原型鏈,原型鏈就是一個實例化了空對象{}進行擴展,並綁定原型鏈上的屬性(可以理解爲new了一個原型鏈)

    prototype.method()執行中,var prototype = fn.prototype 就相當於 var prototype = {},原型鏈上含有很多私有方法和定義屬性等等,

    比如我們定義的fn.prototype.num = 30; fn.prototype.method = function () { console.log(this); }

所以此時this輸出的值是{num: 30,......}

4.method()就相當於window.method(),很顯然,全局環境中的this指向的是window, 全局window對象沒有定義num,所以如果輸出this.num就是undefined

這個實例很大程度依賴小夥伴們對js基礎知識掌握的考驗

總結:在沒有返回值的構造函數中,this指向的是new實例化創建出來的對象,對象中與函數this進行綁定(原型鏈相同原理,參考解析3)

有return返回值的構造函數
看實例

this.num = 10;
return ''
/*
*  '' 空字符串 ---> 10
*  [] 數組 --->undefined
*  {} 對象 ---> 10
*  123 數字 ---> 10
*  function(){} 方法 ---> undefined
*  null  ------> 10
* */

}

var obj = new fn()
console.log(obj.num);

從上述實例中,我們可以

總結:構造函數有返回值,當返回值是對象(type of進行類型查看)的時候,this指向的是實例化創建出來的對象(obj)

            當返回值不是對象的時候,保持原本規則不變,

            此處null是一個特例,返回null時,this指向的是實例化創建出來的對象(obj)

5.箭頭函數環境中的this指向
經常有前端剛入門的小夥伴,this指向發生偏移的時候,有些同伴就說,趕緊用ES6的箭頭函數啊?這樣this的指向就不會變了,至於爲什麼不會變,自己都說不出來的情況

接下來,讓我們剖析一下箭頭箭頭函數

box1.onclick = move1;
box2.onclick = move2;

function move1() {

setTimeout(function () {
  console.log(this);
}, 1000)

}

function move2() {

setTimeout(() => {
  console.log(this);
}, 1000)

}

解析:爲何在setTimeout中使用function(){}和箭頭函數() => {}this的指向不一樣呢?

延遲調用的函數在setTimeout中是以入參的形式調用的

  1.  其實如果傳入的入參提到外面進行定義,不就是如下面一樣嘛,這是不難理解,fn()就是相當於window.fn(),所以this指向window對象

function fn() {
console.log(this)
}

function move1() {
setTimeout(fn, 1000)
}
2.入參以箭頭函數形式進行程序執行,this輸出爲調用的節點了,是不是如下面一樣了

fn: () => {
  console.log(this)
}

}
// obj是不能形成獨立作用域的
// obj.fn() ------> this指向的是window對象

總結:箭頭函數中的this指向官方文檔指的是上下文環境中的this;

           我們理解爲:箭頭函數本身是沒有this和argument的,在箭頭函數中調用this實際上就是在調用定義在上一層作用域的this指向;這裏強調一下,指的是上一層作用域,因爲對象是不能形成獨立的作用域的。

this的指向常用的幾種情況都在這裏了,小夥伴們請認真閱讀,仔細思考才能不被繞暈哦,待看後期廣大借閱者理解程度,如果理解的不是很透徹,筆者可以考慮錄個視頻也無不可哦…………^ - ^

原文地址https://my.oschina.net/u/3419683/blog/4306178

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章