this是困惑JavaScript開發者的一大‘毒瘤’,在開發過程當中,但凡用到this的時候,咱們都會很頭疼,那麼這個this在JavaScript中究竟是怎麼樣的?身爲一個前端coder,這是一個避不開的話題。前端
下面,咱們先開看一段社區裏面的經典的精華代碼,看看它到底試圖說明什麼:app
function foo() { var a = 2; this.bar(); } function bar() { console.log(this.a); } foo(); // ReferenceError: a is not defined
看了這一段代碼,咱們不禁再想一下兩個問題函數
若是this指向函數自身,foo函數中的this.bar()居然神奇的調用成功了,這顯然是不對的。這時候若是打印this,很明顯this指向全局對象window。this
若是this指向詞法做用域,那麼爲何bar()會引起引用錯誤?很明顯,this也並不指向詞法做用域。spa
我想,社區經典代碼,大概就是爲了陳述咱們常常會有的這兩種錯誤的觀念,那麼,咱們這時候不只要思考了,this究竟是什麼?code
說明JavaScript爲何要用this以前,咱們先看看下面代碼:對象
function printName(obj){ console.log(obj.name) } var obj1 = { name: 'obj1Name' } var obj2 = { name: 'obj2Name' } printName(obj1); // obj1Name printName(obj2); // obj2Name
在這段代碼中,若是要打印出某對象的name屬性,那麼就須要顯示的將對象傳遞進入方法參數,那麼若是不想要傳遞參數,有沒有什麼辦法來實現這樣的操做?blog
這個時候,this便應運而生了。看下面代碼:ip
function printName(){ console.log(this.name) } var obj1 = { name: 'obj1Name' } var obj2 = { name: 'obj2Name' } printName.call(obj1); // obj1Name printName.call(obj2); // obj2Name
這就是this出現的緣由,this出現的宗旨是想要在函數調用的時候隱式的傳遞對象引用,想要使得咱們的代碼看起來更優雅,可是this在JavaScript的引入真的完美作到咱們想要的事情了嗎?作用域
不,並無,最起碼this的指向就讓大多數前端開發者頭疼不已,甚至用詞法做用域的概念去避免(self = this or 箭頭函數)。
有上述描述,咱們能夠得出this的一些特色:
一、隱式傳遞對象引用,但並不能穿透做用域(經典代碼的bar)
二、運行時調用纔會產生this
三、this不指向函數自身,也不指向詞法做用域
因此,this究竟是什麼?
當函數調用時,this在運行時綁定,而函數調用此時產生了執行上下文,執行上下文中包括了調用棧、參數信息等,固然也包括了this。this正是爲了在運行時綁定而存在的。
那麼,this的綁定又是怎麼樣的呢,或者說this究竟是指向了什麼?
先說JavaScript的this避坑三連:
一、當函數做爲對象的方法調用的時候,函數中this指向該對象(上述call調用代碼)。
二、當函數被正常調用(全局做用域下),this指向window。(注意:嚴格模式指向undefined)。
三、this不會有函數穿透現象(箭頭函數或者匿名函數除外)
在這裏說說第三點的函數穿透,其實和做用域穿透是相同的概念。那麼爲何箭頭函數和匿名函數能夠穿透到外層this呢?
由於箭頭函數沒有本身的執行上下文,而this又是執行上下文的一個屬性,因此,箭頭函數的this或者說在其中若是調用this的話,其實就是調用的箭頭函數外層的this。
要說this默認指向,只看簡單的一個普通函數調用便可,通常來講,this的默認指向是全局對象。看下面代碼:
var obj = { name: 'objName', printThis: function(){ console.log(this); function innerFunc() { console.log(this); } innerFunc(); } } obj.printThis();
這段代碼中,咱們執行代碼,會發現,innerFunc的this打印出來的居然是全局對象window(嚴格模式下,undefined),同時,這一段代碼也印證了,咱們obj調用函數時候,外層函數的this指向的是obj,而且沒有this指向穿透現象。
那麼有什麼辦法顯式讓對象調用this呢?(最上面的call)
看下面代碼:
var obj = { name: 'objName', printThis: function(){ console.log(this); var self = this; function innerFunc() { console.log(self); } innerFunc(); } } obj.printThis();
這一段代碼,毫無疑問是對this默認指向的代碼的修復(經過self避免,並不是修改this指向,inner中this依舊指向全局對象)
箭頭函數,和self避免的方式差很少,由於箭頭函數沒有執行上下文,它內部的this至關因而外部函數的this。
因此,若是是應用self避免,和箭頭函數,咱們至關因而撇開了this問題,是用詞法做用域來避免咱們不熟悉的東西,若是針對到this,咱們依舊感受到有些頭疼。
那麼,這時候,就須要顯式執行this了。仍是上述代碼,作以下修改:
var obj = { name: 'objName', printThis: function(){ console.log(this); function innerFunc() { console.log(this); } innerFunc.call(obj); // 顯式綁定this } } obj.printThis();
這個時候,咱們打印出來的兩個this也就徹底一致,都是obj對象,由於在這裏咱們用call來顯式綁定了this。
除了call,咱們還能夠用bind和apply來實現如此效果。
this在運行時產生,率屬於函數上下文的一個屬性,this的指向不一而定,是在函數調用時根據上下文來肯定,也能夠利用顯式調用的call、apply、bind方式來修改this指向。
this是個不可避免的問題,當咱們用箭頭函數和self避免的時候,也要思考一下this的指向,以避免混淆了this和詞法做用域的概念。