文章目录加载中

JavaScript基础-call、apply、bind实现

感谢 @purain 的指正,callapply 更完美的实现要借助 Symbol。具体请看Issue

# 实现 call

来看下call的原生表现形式:

function test(arg1, arg2) {
    console.log(arg1, arg2);
    console.log(this.a, this.b);
}
test.call(
    {
        a: "a",
        b: "b"
    },
    1,
    2
);

好了,开始手动实现我们的call2。在实现的过程有个关键:

如果一个函数作为一个对象的属性,那么通过对象的**.**运算符调用此函数,**this**就是此对象

let obj = {
    a: "a",
    b: "b",
    test: function(arg1, arg2) {
        console.log(arg1, arg2);
        // this.a 就是 a; this.b 就是 b
        console.log(this.a, this.b);
    }
};
obj.test(1, 2);

知道了实现关键,下面就是我们模拟的call

Function.prototype.call2 = function(context) {
    if (typeof this !== "function") {
        throw new TypeError("Error");
    }
    // 默认上下文是window
    context = context || window;
    // 保存默认的fn
    const { fn } = context;
    // 前面讲的关键,将函数本身作为对象context的属性调用,自动绑定this
    context.fn = this;
    const args = [...arguments].slice(1);
    const result = context.fn(...args);
    // 恢复默认的fn
    context.fn = fn;
    return result;
};
// 以下是测试代码
function test(arg1, arg2) {
    console.log(arg1, arg2);
    console.log(this.a, this.b);
}
test.call2(
    {
        a: "a",
        b: "b"
    },
    1,
    2
);

# 实现 apply

applycall实现类似,只是传入的参数形式是数组形式,而不是逗号分隔的参数序列。

因此,借助 es6 提供的...运算符,就可以很方便的实现数组和参数序列的转化。

Function.prototype.apply2 = function(context) {
    if (typeof this !== "function") {
        throw new TypeError("Error");
    }
    context = context || window;
    const { fn } = context;
    context.fn = this;
    let result;
    if (Array.isArray(arguments[1])) {
        // 通过...运算符将数组转换为用逗号分隔的参数序列
        result = context.fn(...arguments[1]);
    } else {
        result = context.fn();
    }
    context.fn = fn;
    return result;
};
/**
 * 以下是测试代码
 */
function test(arg1, arg2) {
    console.log(arg1, arg2);
    console.log(this.a, this.b);
}
test.apply2(
    {
        a: "a",
        b: "b"
    },
    [1, 2]
);

# 实现 bind

bind的实现有点意思,它有两个特点:

  • 本身返回一个新的函数,所以要考虑new的情况
  • 可以“保留”参数,内部实现了参数的拼接
Function.prototype.bind2 = function(context) {
    if (typeof this !== "function") {
        throw new TypeError("Error");
    }
    const that = this;
    // 保留之前的参数,为了下面的参数拼接
    const args = [...arguments].slice(1);
    return function F() {
        // 如果被new创建实例,不会被改变上下文!
        if (this instanceof F) {
            return new that(...args, ...arguments);
        }
        // args.concat(...arguments): 拼接之前和现在的参数
        // 注意:arguments是个类Array的Object, 用解构运算符..., 直接拿值拼接
        return that.apply(context, args.concat(...arguments));
    };
};
/**
 * 以下是测试代码
 */
function test(arg1, arg2) {
    console.log(arg1, arg2);
    console.log(this.a, this.b);
}
const test2 = test.bind2(
    {
        a: "a",
        b: "b"
    },
    1
); // 参数 1
test2(2); // 参数 2
本文来自心谭博客:xin-tan.com,经常更新web和算法的文章笔记,前往github查看目录归纳:github.com/dongyuanxin/blog