阅读 339
JavaScript 实现call、apply、bind函数

JavaScript 实现call、apply、bind函数

1. call、apply、bind三者的异同

  • 共同点:都可以改变this指向

  • 不同点:

    • call()apply() 会调用函数,并且改变函数内部this指向.
    • call()apply()传递的参数不一样,call()传递参数使用逗号隔开apply()使用数组传递
    • bind() 不会调用函数,可以改变函数内部this指向
  • 应用场景

    1. call()经常做继承
    2. apply()经常跟数组有关系,比如借助于数学对象实现数组最大值最小值
    3. bind()不调用函数,但是还想改变this指向,比如改变定时器内部的this指向
call()apply()bind()
相同点改变函数this指向改变函数this指向改变函数this指向
是否调用函数
传递参数逗号,隔开数组形式[]逗号,隔开
应用场景继承与数组有关不想调用函数

2. 实现call

  • 将第一个参数作为call函数内部临时对象obj
  • obj一个属性fn,成为实际执行函数,并将this关键字指向这个属性
  • 执行这个函数,并拿到返回值
  • 删除函数属性
  • 返回函数执行的结果
// 实现call
Function.prototype.myCall = function (obj, ...args) {
    // 判断上下文
    const newObj = obj ? Object(obj) : global;
    // 将函数设置为对象的属性
    newObj.fn = this;
    // 执行这个函数,并拿到返回值
    const res = newObj.fn(...args);
    // 删除这个函数属性
    delete newObj.fn;
    // 返回值
    return res;
};
复制代码

3. 实现apply

apply第二个参数是以数组形式传递的,所以基本步骤与call一致,不同的是函数执行的时候需要进行判断是否传入了第二个参数。如果有,将其传入并执行;若没有,直接执行。

// 实现apply
Function.prototype.myApply = function (obj, arr) {
    // 判断上下文
    const newObj = obj ? Object(obj) : global;
    // 将函数设置为对象的属性
    newObj.fn = this;
    // 执行这个函数,并拿到返回值
    let res;
    if (arr) {
        res = newObj.fn(...arr);
    } else {
        res = newObj.fn();
    }
    // 删除这个函数属性
    delete newObj.fn;
    // 返回值
    return res;
};
复制代码

4. 实现bind

// 实现bind
Function.prototype.MyBind = function (context) {
    // 调用的方法本身
    const self = this;
    // 类数组->真数组
    const args = Array.prototype.slice.call(arguments, 1);
    // 中转函数
    const temp = function () {};
    const fn = function () {
        // 将新函数执行时的参数arguments数组化,然后与绑定时的参数合并
        const newArgs = Array.prototype.slice.call(arguments);
        // 如果被new调用,this应该是fn的实例
        return self.apply(this instanceof fn ? this : context || global, args.concat(newArgs));
    };
    // 中转原型链
    temp.prototype = self.prototype;
    fn.prototype = new temp();
    return fn;
};
复制代码

源代码:「JavaScript手撕代码」

文章分类
前端
文章标签