call、apply、bind的区别

call()apply()bind()方法的做用都是改变函数运行时this的指向。 bind() 是返回对应的函数,便于稍后调用;call()apply()则是当即调用。数组

相同点app

  1. 都是用来改变函数执行时this的指向,即函数运行时的上下文;
  2. 第一个参数都是this要指向的对象;
  3. 均可以利用后续参数传参。

一、apply、call

两者做用彻底相同,只是接收参数上的不一样。若是不传入第一个参数,则默认上下文是 window函数

1)call
语法
  • fun.call(context, arg1,arg2...)
  • context: 在fun函数中运行时指定的this值
  • arg1,arg2:指定的参数列表
实现原理
Function.prototype.myCall = function(context, ...args) {
    if( typeof this !== 'function') {
        throw new TypeError('Error') // 判断是不是函数调用
    }
    context = context ? Object(context) : window // 原始值的this会指向该原始值的实例对象 所以须要Object转换成对象类型
    context.fn = this // 函数的this指向隐式绑定到context上
    const result = context.fn(...args) // 经过隐式绑定函数并传递参数
    delete context.fn // 删除上下文对象的属性
    return result // 返回函数执行结果
}

示例this

let test = {
    name: "test"
}
let  fun = {
    fn: function () {
          console.log(this.name)
    }
}
fun.fn.myCall(test) // test
2)apply
语法
  • fun.apply(context, [argsArr])
  • context:在fun 函数运行时指定的this值
  • argsArr:一个数组或者是类数组对象,其中数组中元素会做为单独参数传给fun。当参数值为null 或者undefined时,表示不须要传入任何参数。
注意: 上述指定的this值(thisArg)并不必定是该函数执行时真正的this值,若是该函数处于 非严格模式下,则指定为null或者undefined时会自动指向全局对象(浏览中就是window对象),同时值为原始值(number、string、boolean)的this会指向该原始值的自动包装对象。
实现原理
Function.prototype.myApply = function(context) {
    if( typeof this !== 'function' ) {
        throw new TypeError('Error')
    }
    context = context ? Object(context) : window
    context.fn = this
    let result
    if(arguments[1]) { // 处理参数上和call有区别
        result = context.fn(...arguments[1])
    } else {
        result = context.fn()
    }
    
    delete context.fn
    return result
}

示例prototype

function fn() {
    console.log(this.age)
}

let person = {
    age: 12
}
fn.myApply(person) // 12

二、bind

bind方法会建立一个新函数,称为绑定函数,当调用这个绑定函数时,绑定函数会以建立它时传入bind()的第一个参数做为this,传入bind方法的 其余参数以及和绑定函数运行时自己的参数 ,将按照顺序做为原函数的参数来调用原函数。code

语法
  • fun.bind(context, arg1, arg2, ...)
  • context:当绑定函数被调用时,该参数会做为原函数运行时的this指向;当使用new操做符调用绑定函数时,该参数无效。
  • arg1,arg2...:绑定函数被调用时,这些参数将位于实参以前传递给绑定的方法。
实现原理
Function.prototype.myBind = function(context) {
    if( typeof this  !== 'function' ) {
        throw new TypeError('Error')
    }
    
    let args = [...arguments].slice(1) // bind 函数中的参数
    let self = this // 保存原函数
    return function() {
        return self.apply(context, args.concat(...arguments)) // arguments 是外部函数传入的
    }
}

示例对象

let obj = {
    a: 'a'
};

function f(b, c, d) {
    console.log(this.a, b, c, d)
}

f.myBind(obj, 'b')('e') // a b e undefined
f.myBind(obj, 'b', 'c')('e') // a b c e
f.myBind(obj, 'b', 'c', 'd')('e') //a b c d