細說apply call和bind

bind

概述

bind方法是綁定在了Function.prototype上。這個方法會建立一個新的函數,當被調用時,會將其this關鍵字,設置爲一個提供的值。數組

bind()會建立一個新的函數,成爲綁定函數,目標函數和綁定函數有共同的函數體。新的目標函數被調用的時候,this就會綁定到bind()函數的第一個參數上,且該參數不能被重寫。在函數調用的時候,也能夠爲目標函數增長新的參數,參數跟在第一個參數以後。app

bind綁定this指向

var a = 'hello 222'
var obj = {
    'a':'hello 111',
    say:function(){
        return this.a
    }
}


var otherSay = obj.say


console.log(obj.say())       //hello 111
console.log(otherSay())      //hello 222

咱們都知道,this的指向取決於函數的調用而不是生命的位置,因此,otherSay()被調用的this指向全局對象,這時候a並非obj裏邊定義的。因此會輸出'hello 222'。函數

咱們可使用bind來改變otherSay()的this指向。固然call和apply也能作到。可是bind和另外兩個方法是不同的。this

var newSay = otherSay.bind(obj)
console.log(newSay())

綁定函數newSay(),目標函數otherSay().用bind執行完操做後,兩個函數共用一樣一個執行環境,無論怎麼調用,兩個函數都有一樣的this值。prototype

背後的實現

bind是綁定在了Function的原型上。它是ES5提出來的,用了ES3的apply做爲背後的實現。code

Function.prototype.bind = function(obj){
    var _self = this
    return function(){
        return    _self.apply(obj)
    }
}

當新的目標函數被建立的時候,目標函數的this的值經過apply被設成了傳入的參數的值。由於,咱們傳入咱們想要的函數上下文,當函數調用的時候this就會指向了第一個參數。對象

函數柯里化

函數柯里化的概念是隻傳給函數一部分參數就能調用他,讓他返回一個新的函數去處理另外的參數。
function add(x){
    return function(y){
        return x+y
    }
}

var add1 = add(10)
add1(20)          //30
add1(-10)         // 0

用bind()能夠實現函數的柯里化get

function gender,age,name){
    var salutation = gender === 「male」 ? 「Mr」 : "Ms"
    if(age > 25){
        return "hello" + salutation + name
    } else{
        return "hey" + name
    }
}

這是一個很簡單的函數,很好理解。原型

接下來,咱們使用柯里化greet()方法.bind()接收的第一個參數指定的this的值。回調函數

var greetMaleAdult = greet.bind(null,'name',44)
greetMaleAdult('Peter')

var greetChild = greet(null,'',12)
greetChild('Bob')

使用bind函數,能夠將greet函數柯里化,咱們只須要指定最後一個參數來執行柯里化以後的新函數,由於前邊兩個參數,d都在greet裏邊定義好了。

apply和call

概述

這兩個方法放在一塊兒將,由於兩個函數基本上沒有區別。
兩個函數都是寫到了Function.prototype上面。本質上這兩個方法能夠幫助咱們實現函數借用和指定函數的this值。apply()容許咱們傳入一個參數數組來傳給函數,供這個函數使用。

改變this

當咱們使用這兩個函數的時候,傳入的第一個參數做爲this的目標指向。

var age = 11
function showAge(){
    console.log(this.age)
}

var Man = {
    age:15
}
showAge.apply(Man)      // 當即返回15

咱們看到,加了一行代碼,咱們改變了showAge中的this指向,開始的時候,全局函數showAge中的指向是全局對象,可是,咱們經過apply函數,把this指向了一個對象Man,這個時候,會當即返回函數的執行結果15

apply和bind不同的地方在於,bind不會當即返回結果,會返回一個新的函數,咱們調用這個函數,纔會返回結果
var age = 11
function showAge(){
    console.log(this.age)
}

var Man = {
    age:15
}

showAge.bind(Man)    // 並不會當即執行
showAge.bind(Man)()   // 執行返回的函數,顯示15

另外,在回調函數中,咱們也能夠經過apply或者call方法手動設置this的指向。

var obj = {
    fullname:'not set',
    showName:function(firstname,lastname){
        this.fullname = firstname + ' ' +lastname
    }
}

function getFullname(firstname,lastname,callback,callbackobj){
    callback.call(callbackobj,firstname,lastname)   
}

getFullname('zhang','heihei',obj.showName,obj)
console.log(obj.fullname)   // zhang heihei

函數借用

call和apply的存在可讓咱們借用其餘對象的函數來處理。最典型的例子就是咱們能夠經過函數借用來讓類數組對象使用數據的方法。

var arrayLikeObj = {
    '0':'haha',
    '1':1,
    '2':true,
    '3':[1,2,3],
    length:4
}

var newArr = Array.prototype.slice.call(arrayLikeObj,0)  // ['haha',1,true,Array[3]]

var index = Array.prototype.indexOf.apply(arrayLikeObj,1)   //1

這樣操做的好處就是既能夠保留對象上的那些屬性,又能夠隨時使用數組的方法。

總結

三個函數的最大區別在於:bind函數是返回一個函數(內部實現也是用的apply)供後續調用,而call和apply是當即調用返回執行結果。

另外,在箭頭函數中,apply和call會失效,由於,箭頭函數的this是肯定的,是所在的執行上下文,而不是調用的時候的函數使用者。

相關文章
相關標籤/搜索