bind

概述

bind方法是绑定在了Function.prototype上。这个方法会创建一个新的函数,当被调用时,会将其this关键字,设置为一个提供的值。

bind()会创建一个新的函数,成为绑定函数,目标函数和绑定函数有共同的函数体。新的目标函数被调用的时候,this就会绑定到bind()函数的第一个参数上,且该参数不能被重写。在函数调用的时候,也可以为目标函数增加新的参数,参数跟在第一个参数之后。

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和另外两个方法是不一样的。

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

绑定函数newSay(),目标函数otherSay().用bind执行完操作后,两个函数共用同样一个执行环境,不管怎么调用,两个函数都有同样的this值。

背后的实现

bind是绑定在了Function的原型上。它是ES5提出来的,用了ES3的apply作为背后的实现。

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()可以实现函数的柯里化

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是确定的,是所在的执行上下文,而不是调用的时候的函数使用者。


张小草1018
285 声望8 粉丝