call、apply、bind的实现你知道吗?

116 阅读1分钟

call

call参数一:需要把调用者的this指向的对象,后续参数:a, b, c 调用者的参数;通过call 把fn的this指向了obj,并且执行了fn函数。

var obj = {
    name: '张三'
}

function fn(a, b, c) {
    console.log(this.name, a, b, c)
}

fn.call(obj, 1, 2, 3)

如果我们想自己让他指向obj,我们可以让函数fn以属性的方式在obj出现, 然后调用执行就行,执行完后删除掉fn

var obj2 = {
    name: '李四',
    fn2: fn2
}

function fn2(arg) {
    console.log(this.name, ...arg)
}

obj2.fn2([1, 2, 3])
delete obj2.fn2
console.log(obj2)
/**
 * 通过这个思路 我们可以自己实现call
*/
Function.prototype.myCall = function(ctx) {
    // 调用者必须是函数
    if(typeof this !== 'function') {
        throw new Error('调用者必须是函数')
    }

    // 找到需要执行的函数的参数
    let arg = [...arguments].slice(1),
        _this = ctx || window, // ctx 第一个参数 -> 对象
        result = null; // 执行的函数可能会有返回值 存起来

    // 给对象新增一个fn属性
    _this.fn = this 

    // 执行fn函数传入参数
    result = _this.fn(...arg)

    // 删除掉给对象新增的fn属性
    delete _this.fn

    return result

}

function fn3(a, b) {
    console.log(this.name, a, b)

    return 'call'
}

console.log(fn3.myCall(obj2, 1, 2))

apply

apply的第二个参数以数组形式传给要执行的函数的

Function.prototype.myApply = function(ctx) {
   if(typeof this !== 'function') {
       throw new Error('调用者必须是函数')
   }

   let arg = [...arguments].slice(1),
       _this = ctx || window,
       result = null;

   _this.fn = this
   result = _this.fn(...arg)

   delete _this.fn

   return result
}

function fn4(arg) {
    console.log(this.name, ...arg)

    return 'call'
}

console.log(fn4.myApply(obj2, [1, 2]))

bind

不会立即执行该函数,改变调用者的this指向 并返回一个函数,传参方式和apply一样。

Function.prototype.myBind = function(ctx) {
   if(typeof this !== 'function') {
       throw new Error('调用者必须是函数')
   }

   let arg = [...arguments].slice(1),
       _this = ctx || window,
       result = null,
       fn = this;

    return function Fn() {
        const result = fn.apply(_this, arg)

        return result
    }
}

function fn5(a, b) {
    console.log(this.name, a, b)

    return 'call'
}

let newFn = fn5.myBind(obj2, 11, 22)

console.log(newFn(), 111)