【javascript】函數中的this的四種綁定形式

來源:互聯網
上載者:User

標籤:思考   這一   展開   調用   獨立   log   code   一點   傳遞   

目錄

  • this的預設綁定
  • this的隱式綁定
    • 隱式綁定下,作為對象屬性的函數,對於對象來說是獨立的
    • 在一串對象屬性鏈中,this綁定的是最內層的對象
  • this的顯式綁定:(call和bind方法)
  • new綁定

 

本文

  javascript中的this和函數息息相關,所以今天,我就給大家詳細地講述一番:javascript函數中的this 一談到this,很多讓人暈暈乎乎的抽象概念就跑出來了, 這裡我就只說最核心的一點——函數中的this總指向調用它的對象,接下來的故事都將圍繞這一點展開   (提醒前排的筒子們準備好茶水和西瓜,我要開始講故事啦!!)  【故事】有一個年輕人叫 "迪斯"(this),有一天,迪斯不小心穿越到一個叫  “伽瓦斯克利”(javascript)的 異世界,此時此刻迪斯身無分文, 他首先要做的事情就是——找到他的住宿的地方 ——調用函數的對象 

 


this的預設綁定

 

【故事——線路1】 如果迪斯(this)直到天黑前都沒有找到能收留自己的住所,他眼看就要過上非洲難民的生活, 這時候, 一位樂善好施的魔法師村長——window救世主一般地出現了:先住在我家吧! 

 

【本文】 當一個函數沒有明確的調用對象的時候,也就是單純作為獨立函數調用的時候,將對函數的this使用預設綁定:綁定到全域的window對象
function fire () {     console.log(this === window)}fire(); // 輸出true

 

上面的例子我相信對大多數人都很簡單,但有的時候我們把例子變一下就會具有迷惑性:
function fire () {  // 我是被定義在函數內部的函數哦!     function innerFire() {  console.log(this === window)      }     innerFire(); // 獨立函數調用}fire(); // 輸出true

 

 函數 innerFire在一個外部函數fire裡面聲明且調用,那麼它的this是指向誰呢? 仍然是window  許多人可能會顧慮於fire函數的範圍對innerFire的影響,但我們只要抓住我們的理論武器——沒有明確的調用對象的時候,將對函數的this使用預設綁定:綁定到全域的window對象,便可得正確的答案了 下面這個加強版的例子也是同樣的輸出true
var obj = {   fire: function () {       function innerFire() {          console.log(this === window)        }        innerFire();   // 獨立函數調用     }}obj.fire(); //輸出 true

 

【注意】在這個例子中, obj.fire()的調用實際上使用到了this的隱式綁定,這就是下面我要講的內容,這個例子我接下來還會繼續講解  【總結】 凡事函數作為獨立函數調用,無論它的位置在哪裡,它的行為表現,都和直接在全域環境中調用無異 this的隱式綁定【故事——線路2】 迪斯(this)穿越來異世界“伽瓦斯克利”(javascript)的時候,剛好身上帶了一些錢,於是他找到一個旅館住宿了下來   

 

當函數被一個對象“包含”的時候,我們稱函數的this被隱式綁定到這個對象裡面了,這時候,通過this可以直接存取所綁定的對象裡面的其他屬性,比如下面的a屬性 
var obj = {     a: 1,      fire: function () {           console.log(this.a)        }}obj.fire(); // 輸出1

 

 現在我們需要對平常司空見慣的的代碼操作做一些更深的思考,首先,下面的這兩段代碼達到的效果是相同的:
// 我是第一段代碼function fire () {      console.log(this.a)}  var obj = {      a: 1,      fire: fire  }obj.fire(); // 輸出1 // 我是第二段代碼var obj = {        a: 1,        fire: function () {             console.log(this.a)         }}obj.fire(); // 輸出1

 

  fire函數並不會因為它被定義在obj對象的內部和外部而有任何區別,也就是說在上述隱式綁定的兩種形式下,fire通過this還是可以訪問到obj內的a屬性,這告訴我們:  1.  this是動態綁定的,或者說是在代碼運行期綁定而不是在書寫期 2.  函數於對象的獨立性, this的傳遞丟失問題  (下面的描述可能帶有個人的情感傾向而顯得不太嚴謹,但這是因為我希望閱讀者儘可能地理解我想表達的意思) 隱式綁定下,作為對象屬性的函數,對於對象來說是獨立的  基於this動態綁定的特點,寫在對象內部,作為對象屬性的函數,對於這個對象來說是獨立的。(函數並不被這個外部對象所“完全擁有”) 我想表達的意思是:在上文中,函數雖然被定義在對象的內部中,但它和“在對象外部聲明函數,然後在對象內部通過屬性名稱的方式取得函數的引用”,這兩種方式在 性質上是等價的而不僅僅是效果上)  定義在對象內部的函數只是“恰好可以被這個對象調用”而已,而不是“生來就是為這個對象所調用的”  借用下面的隱式綁定中的this傳遞丟失問題來說明:
var obj = {      a: 1,    // a是定義在對象obj中的屬性   1      fire: function () {   console.log(this.a)        }      } var a = 2;  // a是定義在全域環境中的變數    2var fireInGrobal = obj.fire;  fireInGrobal(); //  輸出 2

 

 上面這段簡單代碼的 有趣之處在於: 這個於obj中的fire函數的引用( fireInGrobal)在調用的時候,行為表現(輸出)完全看不出來它就是在obj內部定義的其原因在於:我們隱式綁定的this丟失了!! 從而 fireInGrobal調用的時候取得的this不是obj,而是window 上面的例子稍微變個形式就會變成一個可能困擾我們的bug: 
var a = 2;var obj = {    a: 1,    // a是定義在對象obj中的屬性    fire: function () {          console.log(this.a)     }}  function otherFire (fn) {     fn();}  otherFire(obj.fire); // 輸出2

 

 在上面,我們的關鍵角色是otherFire函數,它接受一個函數引用作為參數,然後在內部直接調用,但它做的假設是參數fn仍然能夠通過this去取得obj內部的a屬性,但實際上, this對obj的綁定早已經丟失了,所以輸出的是全域的a的值(2),而不是obj內部的a的值(1) 在一串對象屬性鏈中,this綁定的是最內層的對象 在隱式綁定中,如果函數調用位置是在一串對象屬性鏈中,this綁定的是最內層的對象。如下所示:
var obj = {      a: 1,      obj2: {           a: 2,           obj3: {                a:3,                getA: function () {                    console.log(this.a)                    }           }       }} obj.obj2.obj3.getA();  // 輸出3

 

 回到頂部this的顯式綁定:(call和bind方法)【故事——線路3】 迪斯(this)穿越來異世界“伽瓦斯克利”(javascript),經過努力的打拚,積累了一定的財富, 於是他買下了自己的房子 

 

上面我們提到了this的隱式綁定所存在的this綁定丟失的問題, 也就是對於 “ fireInGrobal = obj.fire” fireInGrobal調用和obj.fire調用的結果是不同的因為這個函數賦值的過程無法把fire所綁定的this也傳遞過去。這個時候,call函數就派上用場了

 

call的基本使用方式: fn.call(object)fn是你調用的函數,object參數是你希望函數的this所綁定的對象。 fn.call(object)的作用: 1.即刻調用這個函數(fn) 2.調用這個函數的時候函數的this指向object對象 例子:
var obj = {      a: 1,    // a是定義在對象obj中的屬性      fire: function () {         console.log(this.a)      }} var a = 2;  // a是定義在全域環境中的變數  var fireInGrobal = obj.fire;fireInGrobal();   // 輸出2fireInGrobal.call(obj); // 輸出1

 

 原本丟失了與obj綁定的this參數的fireInGrobal再次重新把this綁回到了obj 但是,我們其實不太喜歡這種每次調用都要依賴call的方式, 我們更希望:能夠一次性 返回一個this被永久綁定到obj的fireInGrobal函數,這樣我們就不必每次調用fireInGrobal都要在尾巴上加上call那麼麻煩了。 怎麼辦呢? 聰明的你一定能想到,在fireInGrobal.call(obj)外麵包裝一個函數不就可以了嘛!
var obj = {      a: 1,    // a是定義在對象obj中的屬性      fire: function () {        console.log(this.a)      }} var a = 2;  // a是定義在全域環境中的變數  var fn = obj.fire;var fireInGrobal = function () {    fn.call(obj)   //硬綁定}       fireInGrobal(); // 輸出1

 

如果使用bind的話會更加簡單
var fireInGrobal = function () {    fn.call(obj)   //硬綁定}

 

可以簡化為:
var fireInGrobal = fn.bind(obj);

 

call和bind的區別是:在綁定this到對象參數的同時:  1.call將立即執行該函數 2.bind不執行函數,只返回一個可供執行的函數 【其他】:至於apply,因為除了使用方法,它和call並沒有太大差別,這裡不加贅述  在這裡,我把顯式綁定和隱式綁定下,函數和“包含”函數的對象間的關係比作買房和租房的區別。 

 

因為this的緣故  在隱式綁定下:函數和只是暫時住在“包含對象“的旅館裡面,可能過幾天就又到另一家旅館住了 在顯式綁定下:函數將取得在“包含對象“裡的永久居住權,一直都會”住在這裡“ new綁定【故事】 迪斯(this)組建了自己的家庭,並生下多個孩子(通過建構函式new了許多個對象)  

 

執行new操作的時候,將建立一個新的對象,並且將建構函式的this指向所建立的新對象 
function foo (a) {     this.a = a;} var a1  = new foo (1);var a2  = new foo (2);var a3  = new foo (3);var a4  = new foo (4); console.log(a1.a); // 輸出1console.log(a2.a); // 輸出2console.log(a3.a); // 輸出3console.log(a4.a); // 輸出4

 

 【完】 

 

【javascript】函數中的this的四種綁定形式

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.