在 javascript 中,call、apply和bind 都是为了改变某个函数运行时的上下文(context)而存在的,换句话说,就是为了改变函数体内部 this 的指向。
call、apply

举个例子先:

        let person = {
            name:'Lucy',
            say:function () {
                console.log(this.name)
            }
        }
        person.say() // Lucy

如果我们有个person1、person2、person3,想要调用person的say方法,又不想重新定义say方法,可以这样:

        let person1 = {
            name:'Lily'
        }
        let person2  = {
            name:'Jack'
        }
        let person3 = {
            name:'Cici'
        }
        person.say.call(person1) // Lily
        person.say.apply(person2) // Jack
        person.say.bind(person3)() // Cici

所以,可以看出 call、apply、bind 是为了动态改变 this 而出现的,当一个 object 没有某个方法(本栗子中person1、person2、person3没有say方法),但是其他的有,我们可以借助call或apply或bind用其它对象的方法来操作。

call、apply、bind的区别

对于call和apply而言,他们的作用是一样的,区别在于接收的参数不一样,call 需要把参数按顺序传递进去,而 apply 则是把参数放在数组里,例如:

        let person = {
            name:'Lucy',
            say:function (a,b) {
                console.log(a+this.name+b)
            }
        }
        person.say('<','>') // <Lucy>
        let person1 = {
            name:'Lily'
        }
        let person2  = {
            name:'Jack'
        }
        let person3 = {
            name:'Cici'
        }
        person.say.call(person1,'<','>') // <Lily>
        person.say.apply(person2,['<','>']) // <Jack>
        person.say.bind(person3,'<','>')() // <Cici>

对于bind而言,我们可以看到用bind调用函数,后面还需要一个括号。MDN的解释是:bind()方法会创建一个新函数,称为绑定函数,当调用这个绑定函数时,绑定函数会以创建它时传入 bind()方法的第一个参数作为 this,传入 bind() 方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数。也就是说,区别是,当你希望改变上下文环境之后并非立即执行,而是回调执行的时候,使用 bind() 方法。而 apply/call 则会立即执行函数。

call、apply常用用法

1、数组之间追加

        let arr1 = [1,2,3]
        let arr2 = [4,5,6]
        Array.prototype.push.apply(arr1,arr2) //[1,2,3,4,5,6]

2、获取数组中的最大值和最小值

        let numbers = [-1,10,20,101]
        let max = Math.max.call(Math,...numbers) // 101
        let min = Math.min.apply(Math,numbers) // -1

number 本身没有 max 方法,但是 Math 有,我们就可以借助 call 或者 apply 使用其方法。
3、验证是否是数组(前提是toString()方法没有被重写过)

        function isArray(obj){
            return Object.prototype.toString.call(obj) === '[object Array]'
        }

4、类(伪)数组使用数组方法

        let domList = Array.prototype.slice.call(document.getElementsByTagName('*'))

Javascript中存在一种名为伪数组的对象结构。比较特别的是 arguments 对象,还有像调用 getElementsByTagName , document.childNodes 之类的,它们返回NodeList对象都属于伪数组。不能应用 Array下的 push , pop 等方法。但是我们能通过 Array.prototype.slice.call 转换为真正的数组的带有 length 属性的对象,这样 domList 就可以应用 Array 下的所有方法了。

js实现bind方法
Function.prototype._bind = function(){
    var self = this   //原函数
    var context = Array.prototype.shift.call(arguments)  //this上下文
    var args = Array.prototype.slice.call(arguments)  //参数
    return function(){
        self.apply(context, args.concat([].slice.call(arguments)))
    }
}

MrBean
20 声望0 粉丝