快速理解JavaScript中this的用法與陷阱

this是 JS 這門語言的魅力之一——靈活方便又難以捉摸,即便是有經驗的程序員,若是不仔細也有可能搞錯,關於this的用法也成爲許多公司的經典面試題。程序員

若是你寫過 Java ,你可能接觸過this——通常指向當前對象,實際上,這時候this的含義已經肯定了,由於Java屬於編譯期綁定,而JS屬於運行期綁定,因此致使this的含義在運行過程當中可能有多種變化。面試

進一步說,this和它聲明環境無關,而徹底取決於他的執行環境。務必牢記這句話。app

//讀如下代碼以前,必須先閱讀《哈利·波特》原著。(笑)

var name = '羅恩';
var aaa = {
    name: '哈利',
    say: function () {
        console.log(this.name);
    }
}

var bbb = {
    name: '赫敏',
    say: aaa.say
}

var ccc = aaa.say;

aaa.say();    //哈利
bbb.say();    //赫敏
ccc();        //羅恩

咱們看第一行,aaa.say()調用的是aaa對象自己的say()方法,此時this指代的是aaa對象自己,因此此時輸出固然就是aaa對象的name屬性值。異步

第二行,bbb.say();輸出赫敏必定和JS新手們的常識不相符,其實只要牢記「this取決於執行環境」就能想明白。bbb對象是怎麼聲明自身的say方法的呢?它只是把aaa對象的say方法引用過來,注意,引用的是一個方法而非一個對象,而aaa.say存儲的是一個匿名函數,因此這種寫法和如下代碼並無什麼區別。函數

var bbb = {
    name: '赫敏',
    say: function () {
        console.log(this.name);
    }
}

第三行的ccc()是在最外層執行,也就是在全局對象window下。因此ccc()執行的時候this指代的就是window對象。而在window對象下聲明瞭name屬性,就至關於window.name = '羅恩',輸出的固然就是羅恩this

固然,也有特殊狀況,那就是 setTimeout 和 setInterval 。
我把開頭的aaa對象的聲明改爲:code

var aaa = {
    name: '哈利',
    getName: function () {
        setTimeout(function(){
            console.log(this.name);
        },100)
    }
}

僅僅是在console.log(this.name)外面套了一個setTimeout,猜猜原來三行的輸出會是什麼?對象

答案:3個羅恩
也就是說,三次this,指代的都是window對象。ip

關於爲何會這樣,我這裏暫時不詳細展開,由於涉及到JS異步回調的知識,若是你僅僅想快速熟悉this的用法,那麼只要記住這個特殊狀況便可。這個知識點曾經是阿里仍是小米的面試題。get

顯然,三個羅恩不是我想要的,畢竟韋斯萊夫人的孩子已經夠多了。那麼咱們只需稍微改寫一下這個方法:

getName: function () {
        //在setTimeout外存儲this指代的對象
        var that = this;
        setTimeout(function(){
            //this.name變成了that.name
            console.log(that.name);
        },100)
    }

輸出就又正常了。

顯然,that並非一個關鍵字,只是一個你們解決這種狀況時約定俗成的名字。若是你願意,也能夠叫thatGuy。固然,考慮到可能會有其餘人維護你的代碼,仍是用that比較好。

之因此寫這篇文章,是爲了我下一篇文章作鋪墊:
《快速理解JavaScript中apply()和call()的用法》敬請期待~~

相關文章
相關標籤/搜索