1. 程式人生 > >call,apply,bind的作用及使用場景

call,apply,bind的作用及使用場景

網上講這些的文章數不勝數,之前也看過有理解一些。前段時間在工作中遇到了一個場景又用到了,正好來重新總結一下,整理思路,加強記憶。

call,apply,bind的作用

js在開發的過程中經常會用到this,來表示當前執行環境本身,通過this來取得當前環境下的各種資料和方法。但是,有時候我們想要讓a物件借b物件的方法用一下,要怎麼辦呢?例如:

var person = {
    name: '文德嗣',
    sayName: function(){
        console.log(this.name)
    }
}
var person1 = {
    name: '馬千矚'
} //如果我們想讓文德嗣叫一下馬路對面的馬千矚,咋辦呢? //只要call他一下就可以啦 person.sayName.call(person1) //同樣的,使用apply和bind也可以達到相同的效果: person.sayName.apply(person1) //下邊會說,為什麼bind後邊多了個() person.sayName.bind(person1)()

call,apply,bind的關係

上邊的例子我們知道了他們的作用,接下來說說他們的區別:
call和apply的作用是完全一樣的,唯一的區別在於他們接受引數的方式。上邊的例子只有一個環境引數,所以看不出區別,看下邊這個:

var func = function(arg1, arg2) {
     //do something
}
func.call(this, arg1, arg2);
func.apply(this, [arg1, arg2])

其中this是你想要指定的上下文環境,就是一開始那個例子中的person。後邊有多個引數的時候,call需要按順序傳遞引數,而apply只要傳遞一個引數陣列就可以了。
js中引數數量是不定的,所以當引數數量已知固定時,用call,否則用apply,傳入陣列的長度就隨意了。

bind

bind() 方法與 apply 和 call 很相似,其區別在於,call和apply會直接執行方法,文德嗣會當場叫馬千矚。而bind則是生成了一個新方法,不會自動執行。所以在一開始的例子中才會再後邊多加一個括號,讓他執行。舉個例子:

var foo = {
    bar : 1,
    eventBind: function(){
        $('.someClass').on('click',function(event) {
            /* Act on the event */
            console.log(this.bar);      //1
        }.bind(this));
    }
}

上邊綁定了一個新事件,再將時間綁到foo物件上就可以在執行事件函式的時候讀取this.bar了。

當多次bind的時候,會怎麼樣呢?

var bar = function(){
    console.log(this.x);
}
var foo = {
    x:3
}
var sed = {
    x:4
}
var func = bar.bind(foo).bind(sed);
func(); //3

var fiv = {
    x:5
}
var func = bar.bind(foo).bind(sed).bind(fiv);
func(); //3

答案是,兩次都仍將輸出 3 ,而非期待中的 4 和 5 。原因是,在Javascript中,多次 bind() 是無效的。更深層次的原因, bind() 的實現,相當於使用函式在內部包了一個 call / apply ,第二次 bind() 相當於再包住第一次 bind() ,故第二次以後的 bind 是無法生效的。

總結:

  • apply 、 call 、bind 三者都是用來改變函式的this物件的指向的;
  • apply 、 call 、bind 三者第一個引數都是this要指向的物件,也就是想指定的上下文;
  • apply 、 call 、bind 三者都可以利用後續引數傳參;
  • bind 是返回對應函式,便於稍後呼叫;apply 、call 則是立即呼叫 。

工作中的使用場景

專案中有一個table元件用來顯示一堆資料,一個delete元件用來刪除table中選中的行中的資料。
但是現在,table中有的資料要求無法刪除,這就需要在delete執行刪除之前,進行判斷。而不同的table中,不能刪除的資料的範圍不同,所以這個判斷要放在table元件裡邊才好根據不同的table來設定判斷條件。
這時候,就要在delete元件中,刪除前呼叫其parent(也就是容納他的table元件)中的deleteConfirmFunc:

//其中this.dodelete是delete元件中的刪除方法,作為回撥函式傳入deleteConfirmFunc
this.parent.deleteConfirmFunc.call(this.parent,this.dodelete)

而在table元件中,編輯deleteConfirmFunc

deleteConfirmFunc: function(data, cb) {
    var deleteable = data.deletable
    if (deleteable) {
      cb.call(this.actionDelete);
    } else {
      bootbox.alert("無法刪除已付款訂單!")
    }
}

呼叫cb的時候,再call一下,使用delete的上下文環境。

這樣就實現了。