原生js手写实现call,apply与bind

259 阅读1分钟

实现call

// call模拟
Function.prototype.my_call = function (obj) {
    //判断是否为null或者undefined,同时考虑传递参数不是对象情况
    obj = obj ? Object(obj) : window;
    var args = [];
    // 注意i从1开始
    for (var i = 1, len = arguments.length; i < len; i++) {
        args.push("arguments[" + i + "]");//保证传入的参数为属性值,值得这样复杂的写法
    };
    obj.fn = this; // 此时this就是函数fn
    var result = eval("obj.fn(" + args + ")"); // 执行fn,数组与字符串相加隐性执行数组转字符串
    delete obj.fn; //删除fn
    return result;
};
// ES6 call
Function.prototype.my_call = function (obj) {
    obj = obj ? Object(obj) : window;
    obj.fn = this;
    // 利用拓展运算符直接将arguments转为数组
    let args = [...arguments].slice(1);
    let result = obj.fn(...args);

    delete obj.fn
    return result;
};

实现apply

// apply模拟
Function.prototype.my_apply = function (obj, arr) {
    obj = obj ? Object(obj) : window;
    obj.fn = this;
    var result;
    if (!arr) {
        result = obj.fn();
    } else {
        var args = [];
        // 注意这里的i从0开始
        for (var i = 0, len = arr.length; i < len; i++) {
            args.push("arr[" + i + "]");
        };
        result = eval("obj.fn(" + args + ")"); // 执行fn
    };
    delete obj.fn; //删除fn
    return result;
};
// ES6 apply
Function.prototype.my_apply = function (obj, arr) {
    obj = obj ? Object(obj) : window;
    obj.fn = this;
    let result;
    if (!arr) {
        result = obj.fn();
    } else {
        result = obj.fn(...arr);
    };

    delete obj.fn
    return result;
};

实现bind方法

Function.prototype.my_bind = function() {
    var self = this, // 保存原函数
    context = Array.prototype.shift.call(arguments), // 保存需要绑定的this上下文
    // 上一行等价于 context = [].shift.call(arguments);
    args = Array.prototype.slice.call(arguments); // 剩余的参数转为数组
    return function() { // 返回一个新函数
        self.apply(
            context,
            Array.prototype.concat.call( args , Array.prototype.slice.call(arguments) )
        );
    }
}