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也能夠改變函數內部的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指向而已,區別僅僅是使用的方法和場景不一樣。事件