關於JS中this指向問題的探究

寫在前面

本篇文章的所有例子來源都是《JS設計模式與開發實踐》這本書,寫這篇文章之前也去查閱了很多關於this指向問題的探討,包括但不僅僅有像阮一峯老師,還有很多的博主的帖子,還是決定寫這篇文章有以下幾個原因,第一,加深自己的理解,重新理一遍關於這方面的知識,第二,我儘可能的使用通俗簡單的說辭進行解釋
力求讓更多的人明白這個東西,第三,this是js中的一個關鍵字,很有必要單獨拿出來寫一篇文章。最後一個原因是記錄以下拜讀這本書的過程!

js中的this

this

js中的this總是指向一個對象,也就是一個obj,但是具體指向的是哪一個obj是根據具體的運行時函數的執行環境動態綁定的,而不是函數被聲明的環境!
看完這句話如果不是很明白的話沒關係,因爲這個畢竟只是一個解釋,給出這句話的時候如果你們就明白了,下面我寫的一切都沒意義了,所以不明白是對的,明白了當然更好!

this的指向

如果不考慮常用的with和eval的情況下,具體到實際應用中,this的指向大致可以分爲下面四類:

  • 作爲對象的方法調用
  • 作爲普通函數調用
  • 構造器調用
  • Function.prototype.call 或者 Function.prototype.apply調用
    下面我們一個一個說
作爲對象的方法調用
var obj = {
        a : 1,
        getA:function () {
            console.info(this === obj) // true
            console.info(this.a)   // 1
        }
    }
    obj.getA()
作爲普通函數調用

當我們不把函數作爲一個對象的屬性被調用時,也就是我們常見的普通函數使用的時候,此時的this其實指向的是當前的全局對象,也就是windows,因爲在js中全局對象就是windows

    window.name = "globalName"
    var getName = function () {
        return this.name
    }
    console.info(getName()) //globalName
  • 或者這樣寫
    window.name = "globalName"
    var obj = {
        name: 'seven',
        getName: function () {
            return this.name
        }
    }
    var getName = obj.getName;
    console.info(getName()) //globalName

但是當我們使用一個函數調用的時候裏面有一個局部的callback的方法,callback被當作普通函數被調用的時候,根據前面說的callback這個時候的this其實指向的是windows
例如:

window.id = "windows"
    document.getElementById('div1').onclick = function () {
        console.info(this.id) //div1
        var callback = function () {
            console.info(this.id) //windows
        }
        callback()
    }

這個時候我們想callback裏面的this指向不發生改變的話,就需要將this的值重新指向爲當前

window.id = "windows"
document.getElementById('div1').onclick = function () {
        console.info(this.id) //div1
        var that = this;
        var callback = function () {
            console.info(that.id) //div1
        }
        callback()
    }

其實這種寫法如果你使用過一些框架或者是寫過一些這種情況下的js代碼的話,是很好理解的。

作爲構造器調用

構造器看起來是和函數一樣的,他們的區別在於被調用的方式不一樣,當使用new調用的時候他總會返回一個對象,那麼一般情況下此時的this指向的就是該對象

var myClass = function () {
        this.name = "seven"
    }
    var obj = new myClass()
    console.info(obj.name) //seven

那麼如果我們按照下面的方式寫的話,可能結果就不一樣了

var myClass = function () {
        this.name = "seven"
        return {
            name: "anna"
        }
    }
    var obj = new myClass()
    console.info(obj.name) //anna

解釋一下爲什麼,因爲此時構造器顯式的返回了一個對象,那麼他的返回值就會被這個新的對象給替換,因爲this指向的是一個對象。也就是說他可以返回,如果不是一個對象的話,那麼this的指向還是不會變

Function.prototype.call 或者 Function.prototype.apply調用
var obj = {
        name : 'seven',
        getName : function () {
        return this.name
        }
    };
    var obj2 = {
        name : 'anna'
    };
    console.info(obj.getName()) //seven
    console.info(obj.getName.call(obj2)) //anna

具體爲什麼會這樣,下篇文章後面會寫關於apply和call的使用,可以簡單的理解爲他可以直接劫持this 的指向,重新給到一個新的對象!

this丟失的情況

先看一段代碼

var obj = {
        myName : 'seven',
        getName : function () {
         return this.myName
        }
    };
    console.info(obj.getName()); //seven
    var getName2 = obj.getName;
    console.info(getName2()) //undefined

當第一次調用的時候,他是作爲obj的屬性被調用,那麼此時的this指向的是這個對象,所以他是有myName的屬性的,但是getName2是來自obj.getName
此時是作爲普通函數調用的,所以此時的this指向的是windows,但是我們windows並沒有聲明任何關於myName的值,所以是undefined

  • 我們再看一種情況
  var getId = document.getElementById
  getId('div1')

這段代碼會報錯,原因是很多的引擎中document.getElementById內部實現是用到this指向的,原本這個this是指向document的,當document.getElementById這個方法被調用的時候this指向也確實是改document
但是當我們使用getId來引用這個,他的this指向指的是windows

  • 修復以後的代碼:
document.getElementById = (function (func) {
        return function () {
            return func.apply(document, arguments)
        }
    })(document.getElementById)
    var getId = document.getElementById
    var div = getId('div1')
    console.info(div.id) //div1

最後

其實this指向的問題和JS中很多別的不好理解的概念差不多,用的多了就明白了爲什麼那麼寫,很多的時候我們看到一個錯,就知道需要使用let that = this類似這樣的代碼塊解決,究其原因是他很理解錯誤的原因嗎?其實是見的比較多了罷了,實踐出真知,共勉!

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