call、apply 以及 bind 的区别和用法

call、apply、bind

call和apply共同点

  • 共同点:改变函数执行时的上下文,将一个对象的方法交给另一个对象来执行,并且是立即执行的
  • call和apply的对象,必须是一个函数Function

call和apply区别

call的写法

Function.call(obj, [param1[, param2[, ...[, paramN]]]])

需要注意以下几点:

  • 调用call的对象,必须是个函数Function
  • call的第一个参数,是一个对象,Function的调用者,将会指向这个对象。如果不传,则默认为全局对象window
  • 第二个参数开始,可以接收任意个参数,每个参数会映射到相应位置的Function的参数上。
  • 但是如果将所有的参数作为数组传入,他们会作为一个整体映射到Function对应的第一个参数上,之后参数都为空
function func(a,b,c) {}
func.call(obj, 1,2,3) // func接收到的参数为1,2,3
func.call(obj, [1,2,3]) // func接收到的参数为[1,2,3], undefined, undefined
复制代码

apply的写法

Function.apply(obj[, argArray])

需要注意的是:

  • 它的调用者必须是函数Function,并且只接受两个参数,第一个参数的规则与call一致
  • 第二个参数,必须是数组或者类数组,他们会被转换成类数组,传入到Function中,并且会被映射到Function对应的参数上,
func.apply(obj, [1,2,3]) // func接收到的参数是1,2,3
func.apply(obj, {0:1, 1:2, 2:3, length: 3}) // func接收到的参数是1,2,3
复制代码

什么是类数组

  • 具备与数组特征类似的对象
  • 类数组无法使用forEach、splice、push等数组原型链上的方法

call和apply的用途

call的使用场景

  • 对象的继承
function superClass() {
    this.a = 1
    this.print = function() {
        console.log(this.a)
    }
}
function subClass() {
    superClass.call(this)
    this.print()
}
subClass() // 1
复制代码
  • 借用方法
    • 类数组,如果想使用Array原型链上的方法
    • let demoNodes = Array.prototype.slice.call(document.getElementsByTagName("*"))

apply 的用法

  • Math.max,用来获取数组中最大的一项
    • let max = Math.max.apply(null, array)
  • 实现两个数组合并
let arr1 = [1, 2, 3]
let arr2 = [4, 5, 6]
Array.prototype.push.apply(arr1, arr2)
console.log(arr1) // [1, 2, 3, 4, 5, 6]
复制代码

bind的使用

bind()方法创建一个新的函数,在调用时设置this关键字为提供的值,并在调用新函数时,将给定参数列表作为原函数的参数序列的前若干项

Function.bind(thisArg, arg1, arg2])

  • bind方法与apply、call比较类似,也能改变函数体内的this指向,不同的是,bind方法的返回值是函数,并且需要稍后调用,才会执行。而apply和call则是立即调用
  • 如果bind的第一个参数是null或者undefined,this就指向全局对象window
function add (a, b) {
    return a + b;
}
function sub (a, b) {
    return a - b;
}
add.bind(sub, 5, 3); // 这时,并不会返回 8
add.bind(sub, 5, 3)(); // 调用后,返回 8
复制代码

总结

  • call 和 apply 的主要作用,是改变对象的执行上下文,并且是立即执行的。它们在参数上的写法略有区别。

  • bind 也能改变对象的执行上下文,它与 call 和 apply 不同的是,返回值是一个函数,并且需要稍后再调用一下,才会执行。

bind的模拟实现

bind

bind()方法创建一个新函数,当这个新函数被调用时,bind()的第一个参数将作为它运行时的this,之后的一序列参数将会在传递的实参钱传入作为它的参数

function fn(a, b, c) {
  return a + b + c;
}
var _fn = fn.bind(null, 10);
var ans = _fn(20, 30); // 60
复制代码

它的特点:

  1. 返回一个函数;
  2. 可以传入参数

实现bind最终代码

Function.prototype.bind1 = function(context) {
    if (typeof this !== 'function') {
        throw new Error('Function.prototype.bind --what is trying to be bound is not callable')
    }
    var self = this
    var args = Array.prototype.slice.call(arguments, 1)
    var fNOP = function () {}
    var fBound = function() {
        var bindArgs = Array.prototype.slice.call(arguments)
         // 当作为构造函数时,this 指向实例,此时结果为 true,将绑定函数的 this 指向该实例,可以让实例获得来自绑定函数的值
        // 以上面的是 demo 为例,如果改成 `this instanceof fBound ? null : context`,实例只是一个空对象,将 null 改成 this ,实例会具有 habit 属性
        // 当作为普通函数时,this 指向 window,此时结果为 false,将绑定函数的 this 指向 context
        return self.apply(this instanceof 
        fNOP ? this : context, args.concat(bindArgs))
    }
    fNOP.prototype = this.prototype
    fBound.prototype = new fNOP()
    return fBound
}

复制代码

参考资料

猜你喜欢

转载自juejin.im/post/7110793865400942623