手写函数call、apply、bind方法

76 阅读3分钟

1、call方法

一、call函数的定义:Function 实例的 call()  方法会以给定的 this 值和逐个提供的参数调用该函数。直接上代码:

// 自定义call方法
function _call(target, ...arg) {
    if (!target) {
        // target对象为null或者undefined时,赋值为全局对象(nodejs为global)
        target = window;
    }
    // 定义一个symbol类型的变量来作为对象的键
    // 这样做的好处是,避免了出现同名属性时,属性值被覆盖的问题
    let symbol = Symbol();
    target[symbol] = this;
    
    let result = target[symbol](...arg);
    delete target[symbol];
    return result;
}

Function.prototype._call = _call;

var name = 'window';
var obj = {
    name: 'obj',
}

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

print._call(obj, '1', '2', '3');  // obj 1 2 3

2、apply方法

一、apply函数的定义:Function 实例的 apply()  方法会以给定的 this 值和作为数组(或类数组对象)提供的 arguments 调用该函数。直接上代码:

// 自定义apply方法
function _apply(target, arg) {
    if (!target) {
        // target对象为null或者undefined时,赋值为全局对象(nodejs为global)
        target = window;
    }
    // 处理arg参数
    // 由于该参数可以是类数组对象,因此,我们先将其处理为数组对象
    let input = Array.from(arg);
    // 定义一个symbol类型的变量来作为对象的键
    // 这样做的好处是,避免了出现同名属性时,属性值被覆盖的问题
    let symbol = Symbol();
    target[symbol] = this;

    let result = target[symbol](...input);
    delete target[symbol];
    return result;
}

Function.prototype._apply = _apply;

var name = 'window';
var obj = {
    name: 'obj',
}

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

print._apply(obj, ['1', '2', '3']);  // obj 1 2 3

tips:Array.from()

3、bind方法

一、bind函数的定义:Function 实例的 bind()  方法创建一个新函数,当调用该新函数时,它会调用原始函数并将其 this 关键字设置为给定的值,同时,还可以传入一系列指定的参数,这些参数会插入到调用新函数时传入的参数的前面。直接上代码:

// 自定义bind方法
function _bind(target, ...arg) {
    if (!target) {
        // target对象为null或者undefined时,赋值为全局对象(nodejs为global)
        target = window;
    }
    // 保留需要被绑定的函数引用
    let _this = this;
    let bindFunc = function (..._arg) {
        // 监测函数是否使用new关键字调用,不懂的同学请自行查看MDN
        if (!new.target) {
            // 此处也可以使用我们手写的call
            return _this.call(target, ...arg, ..._arg);
        } else {
            // 使用new关键字调用绑定后的函数,则this指向new关键字创建的新对象
            // 具体可以参考本人的博客 https://blog.csdn.net/m0_63422186/article/details/130160636
            return _this.call(this, ...arg, ..._arg);
        }
    }
    return bindFunc;
}

Function.prototype._bind = _bind;

var name = 'window';
var obj = {
    name: 'obj',
}

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

let func = print._bind(obj, 0, 0, 0);
func(1, 2, 3); // obj 0 0 0 1 2 3

let newObj = new func('a', 'b', 'c'); // undefined 0 0 0 a b c

总结

  1. call、apply以及bind方法都是改变函数执行时内部this指向。
  2. 调用call以及apply方法都是立即执行,而调用bind方法返回一个新的函数,新函数内部的this指向绑定时传入对象。
  3. call函数与apply函数的区别就是它们传入参数的方式不同。

座右铭

天下难事,必作于易;天下大事,必做于细。