call,apply和bind,其實很簡單

call和apply

call和aplly做用徹底同樣,都是在特定的上下文中調用函數,或者說改變函數內部的this指向;區別僅在於接收參數的方式不一樣。node

var dog = {
  name: "dog"
};
var cat = {
  name: "cat"
};
var sayName = function (age, gender) {
  console.log(this.name + "," + age + "," + gender)
};
sayName(); //undefined
sayName.call(dog, 2, "male"); //dog,2,male
sayName.call(cat, 3, "female"); //cat,3,female
sayName.apply(dog, [2, "male"]); //dog,2,male
sayName.apply(cat, [3, "female"]); //cat,3,female

咱們在全局環境定義了一個sayName方法,調用該方法時會將當前環境的name屬性(this.name)顯示到控制檯上。當咱們在全局環境調用sayName()時,因爲全局環境並無this.name屬性(注意,瀏覽器window對象上有name屬性,這裏指node環境),所以顯示了undefined。
使用call或者apply,就能夠改變函數的執行環境,或者說改變函數內部的this指向。例如sayName.call(dog),表示將sayName內部的this指向了dog對象,所以this.name顯示爲"dog"。當咱們調用sayName.call(cat)時,sayName內部的this又指向了cat對象。
能夠看出來,call與apply的做用徹底相同,區別僅在於接收參數的方式不一樣。數組

fn.call(context, arg1, arg2, ...)
fn.apply(context, [arg1, arg2, ...])

call和apply接收的第一個參數都是函數的運行環境。使用call時傳遞給函數的參數必須逐個傳入,而使用apply時函數的參數應該是一個數組,或者類數組對象(如arguments對象)。至於使用call仍是apply,徹底取決於採用哪一種方法給函數傳遞參數更方便。在不須要給函數傳遞參數的狀況下,使用哪一個方法都無所謂。瀏覽器

bind

bind也能夠改變函數內部的this指向,區別在於bind不會直接調用該函數,而是返回一個綁定了this的函數,由你來決定何時調用。app

var dog = {
  name: "dog"
};
var cat = {
  name: "cat"
};
var sayName = (function (age, gender) {
  console.log(this.name + "," + age + "," + gender)
}).bind(dog, 3, "male");

sayName.call(cat, 2, "female"); //dog,3,male

咱們建立sayName函數時,使用bind將該函數的this指向了dog對象,同時還綁定了傳入函數的參數。
如今咱們調用sayName.call(cat, 2, "female"),發現不管是this對象,仍是傳入函數的參數,都是以前被綁定的值。
bind像call同樣接受多個參數,第一個參數爲context(即內部this值),後面的參數依次爲函數接收的參數函數

用途

call和apply適合用在只須要臨時改變函數運行環境的地方,能夠用來共享一些方法。最經典、實用的莫過於Array.prototype.slice.call(arguments)了,利用call能夠直接在類數組對象上調用Array的slice方法,方便的將類數組對象轉換成真正的數組對象。this

bind方法適合用在將某個函數以值的形式傳遞,同時該函數必須在特定的環境中執行的時候,例如一些事件處理程序,回調函數和setTimeout()、setInterval()等。例如:prototype

var alarmClock = {
  time: function () {
    return new Date();
  },
  alarm: function () {
    console.log(this.time())
  }
};
setInterval(alarmClock.alarm,1000); //TypeError: this.time is not a function
setInterval(alarmClock.alarm.bind(alarmClock),1000); //Thu May 18 2017 12:32:44 GMT+0800 (中國標準時間)

當直接將alarmClock.alarm傳遞給setInterval()時,由於alarmClock.alarm調用的環境是setInterval,即全局環境,其中沒有time方法,因而TypeError。
經過使用bind(alarmClock),就能夠將alarmClock.alarm的運行環境綁定到alarmClock對象上,這樣每次執行均可以得到正確的結果。code

另外,bind還能夠用在函數柯里化。對象

總結

call,apply和bind其實很簡單,無非就是改變了函數內部的this指向而已,區別僅僅是使用的方法和場景不一樣。事件

相關文章
相關標籤/搜索