一文理解javascript中的call、apply 和 bind

·  阅读 473
一文理解javascript中的call、apply 和 bind

1、call和apply的栗子说明

       call和apply都是为了改变某个函数运行时的上下文(context)二存在的,换句话说,就是为了改变函数体内部this的指向。javascript的一大特点是,函数存在[定义时上下文]和[运行时上下文] 以及[上下文是可以改变的]这样的概念。先来一个案例:

function people(){}
people.prototype = {
    name:'小明',
    age:'18',
    say:function(){
        console.log("my name is" + this.name);
    }
}
var peopleTwo = new fruits;
peopleTwo.say(); //my name is 小明
复制代码

      但是如果我们有一个对象 peopleOther = {name:'小红'},我们不想对它重新定义say方法,那么我们可以通过call或apply用peopleTwo的say方法:

peopleOther = {
    name:"小红'
}
peopleTwo.say.call(peopleOther);    //my name is 小红
peopleTwo.say.apply(peopleOther);    //my name is 小红
复制代码

      所以,可以看出call和apply是为了动态改变this而出现的,当一个object没有某个方法(如栗子中peopleOther没有say方法),但是其它对象中有,我们就可以借助call或apply用其它对象的方法实现操作。

2、call和apply的区别

对于apply和call而言,作用完全一样,只是接受参数的方式不太一样:

var myFun = function(arg1,arg2){};
//调用
myFun.call(this,arg1,arg2);
myFun.apply(this,[arg1,arg2]);
复制代码

        其中this就是你想指定的上下文,它可以是任何一个javascript对象(javascript中一切皆对象),call需要把参数按顺序传递进去,而apply则是把参数放在数组里。

3、call和apply的常用用法:

        在javascript中,某些函数的参数数量可能不固定,因此要说使用条件的话,当你的参数是明确知道数量时用call,不确定时用apply。

1、数组之间追加:
    var array1 = [12,'red',{name:'小明'}];
    var array2 = ["demo",'ddd','sss'];
    Array.prototype.push.apply(array1,array2);
    //array1的值为:[12,'red',{name:'小明'},"demo",'ddd','sss']
2、获取数组中的最大值和最小值:
    var numbers = [12,456,24,71,-457];
    var maxNumbers = Math.max.apply(Math,numbers);   //456
    var maxNumbers = Math.max.call(Math,4,5,7,324,345);  //345
    //number本身没有max方法,但是Math有,我们可以借助call或apply使用其方法
3、验证是否是数组(前提是toString()方法没有被重写过):
    functionisArray(obj){
        return Object.prototype.toString.call(obj) === ['object Array'];
    }
4、类(伪)数组使用数组方法:
    var newNodes = Array.prototype.slice.call(document.getElementsByTagName(""));
    //javascript中存在一种名为伪数组的对象结构。比较特别的是arguments对象,还有像调用
    //getElementsByTagName,document.childNodes之类的,他们返回NodeList对象都属于伪数组。
复制代码

4、bind的作用

        bind()方法与apply和call很相似,也是可以改变函数体内this 的指向。bind()方法会创建一个新函数,称为绑定函数,当调用这个绑定函数时,绑定函数会以创建它时传入bind()方法的第一个参数作为this,传入bind()方法的第二个以及后面的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数。

        看个简单的栗子:

    var bar = function(){
        console.log(this.x);
    }
    var foo = {
        x:18
    }
    bar(); //undefined
    var func = bar.bind(foo);
    func(); //18
复制代码

       这里我们创建了一个新的函数func,当使用bind()创建一个绑定函数之后,它被执行的时候,他的this会被设置成foo,而不是像我们调用bar()时的全局作用域。看个有趣的问题,如果连续bind()两次以上,输出的值会是怎么样?像这:

var bar = function(){
    console.log(this.x);
}
var foo = { x:3 };
var goo = { x:6 };
var func = bar.bind(foo).bind(goo);
func(); //?
复制代码

        答案是:3,而非期待的6。原因是,在javascript中,多次bind()是无效的。也就是bind的实现,相当于使用函数在内部包了一个call/apply,第二次bind()相当于再次包住第一次的bind(),所以第二次以后的bind是无法生效的。

5、三者之间的异同

直接看栗子:

var obj = { x:11 }
var foo = {
    getX:function(){
         return this.x;
    }
} 

console.log(foo.getX.bind(obj)());    //11
console.log(foo.getX.call(obj));      //11
console.log(foo.getX.apply(obj));     //11
复制代码

三个唯一的区别就是bind()方法,多个对括号。

也就是说,区别是,当你希望改变上下文环境之后并非立即执行,而是回调执行的时候,使用bind()方法,而apply/call则会立即执行函数。

总结

  1. apply、call、bind三者都是用来改变函数的this对象的指向的;
  2. apply、call、bind三者第一个参数都是this要指向的对象,也就是想指定的上下文;
  3. apply、call、bind三者都可以利用后续参数传参;
  4. bind是返回对应函数,便于稍后调用;apply、call则是立即调用。
分类:
前端
标签:
分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改