call,apply,bind的作用及使用场景

版权声明:本文为博主原创文章,未经博主允许不得转载。原账号CreabineChen现已不用,所有博客均已迁移过来。 https://blog.csdn.net/Creabine/article/details/78295119

网上讲这些的文章数不胜数,之前也看过有理解一些。前段时间在工作中遇到了一个场景又用到了,正好来重新总结一下,整理思路,加强记忆。

参考资料:深入浅出 妙用Javascript中apply、call、bind

call,apply,bind的作用

js在开发的过程中经常会用到this,来表示当前运行环境本身,通过this来取得当前环境下的各种数据和方法。但是,有时候我们想要让a对象借b对象的方法用一下,要怎么办呢?例如:

var person = {
    name: '文德嗣',
    sayName: function(){
        console.log(this.name)
    }
}
var person1 = {
    name: '马千瞩'
}
//如果我们想让文德嗣叫一下马路对面的马千瞩,咋办呢?
//只要call他一下就可以啦
person.sayName.call(person1)
//同样的,使用apply和bind也可以达到相同的效果:
person.sayName.apply(person1)
//下边会说,为什么bind后边多了个()
person.sayName.bind(person1)()

call,apply,bind的关系

上边的例子我们知道了他们的作用,接下来说说他们的区别:
call和apply的作用是完全一样的,唯一的区别在于他们接受参数的方式。上边的例子只有一个环境参数,所以看不出区别,看下边这个:

var func = function(arg1, arg2) {
     //do something
}
func.call(this, arg1, arg2);
func.apply(this, [arg1, arg2])

其中this是你想要指定的上下文环境,就是一开始那个例子中的person。后边有多个参数的时候,call需要按顺序传递参数,而apply只要传递一个参数数组就可以了。
js中参数数量是不定的,所以当参数数量已知固定时,用call,否则用apply,传入数组的长度就随意了。

bind

bind() 方法与 apply 和 call 很相似,其区别在于,call和apply会直接执行方法,文德嗣会当场叫马千瞩。而bind则是生成了一个新方法,不会自动执行。所以在一开始的例子中才会再后边多加一个括号,让他执行。举个例子:

var foo = {
    bar : 1,
    eventBind: function(){
        $('.someClass').on('click',function(event) {
            /* Act on the event */
            console.log(this.bar);      //1
        }.bind(this));
    }
}

上边绑定了一个新事件,再将时间绑到foo对象上就可以在执行事件函数的时候读取this.bar了。

当多次bind的时候,会怎么样呢?

var bar = function(){
    console.log(this.x);
}
var foo = {
    x:3
}
var sed = {
    x:4
}
var func = bar.bind(foo).bind(sed);
func(); //3

var fiv = {
    x:5
}
var func = bar.bind(foo).bind(sed).bind(fiv);
func(); //3

答案是,两次都仍将输出 3 ,而非期待中的 4 和 5 。原因是,在Javascript中,多次 bind() 是无效的。更深层次的原因, bind() 的实现,相当于使用函数在内部包了一个 call / apply ,第二次 bind() 相当于再包住第一次 bind() ,故第二次以后的 bind 是无法生效的。

总结:

  • apply 、 call 、bind 三者都是用来改变函数的this对象的指向的;
  • apply 、 call 、bind 三者第一个参数都是this要指向的对象,也就是想指定的上下文;
  • apply 、 call 、bind 三者都可以利用后续参数传参;
  • bind 是返回对应函数,便于稍后调用;apply 、call 则是立即调用 。

工作中的使用场景

项目中有一个table组件用来显示一堆数据,一个delete组件用来删除table中选中的行中的数据。
但是现在,table中有的数据要求无法删除,这就需要在delete执行删除之前,进行判断。而不同的table中,不能删除的数据的范围不同,所以这个判断要放在table组件里边才好根据不同的table来设定判断条件。
这时候,就要在delete组件中,删除前调用其parent(也就是容纳他的table组件)中的deleteConfirmFunc:

//其中this.dodelete是delete组件中的删除方法,作为回调函数传入deleteConfirmFunc
this.parent.deleteConfirmFunc.call(this.parent,this.dodelete)

而在table组件中,编辑deleteConfirmFunc

deleteConfirmFunc: function(data, cb) {
    var deleteable = data.deletable
    if (deleteable) {
      cb.call(this.actionDelete);
    } else {
      bootbox.alert("无法删除已付款订单!")
    }
}

调用cb的时候,再call一下,使用delete的上下文环境。

这样就实现了。

阅读更多

没有更多推荐了,返回首页