Javascript模拟实现call、apply、bind JavaScript实现call、apply和bind

var foo = {
    value: 1
};
function bar() {
    console.log(this.value);
}
bar.call(foo); // 1  

类似于

var foo = {
    value: 1,
    bar: function () {
        console.log(this.value);
    }
}
foo.bar() // 1

Javascript模拟实现call可以分三步实现:

第一步模拟添加属性方法

foo.fn = bar;

第二步调用方法

foo.fn()

第三步删除属性方法

delete foo.fn

ES3模拟实现call

Function.prototype.call = function (context) {
    // 如果context是null,作用域就是window
    context = context || window;
    // 添加方法
    context.fn = this;
    var args = [];
    // 下面没有直接把arguments[i]push给args,是因为eval会先对args.toString(),toString后arguments[i]就变成了一个没有声明的变量,就会出错
    for (var i = 1; i < arguments.length; i++) {
        args.push('arguments[' + i +']');
    }
    // args = ['arguments[1]', arguments['2'],....];
    // 调用方法
    var result = eval('context.fn(' + args + ')');
    // 删除方法
    delete context.fn;
    return result;
}

ES6模拟实现call

Function.prototype.call = function (context, ...args) {
    context = context || window;
    let fn = Symbol();
    context[fn] = this;
    const result = context[fn](...args);
    Reflect.deleteProperty(context, fn);
    return result;
}

ES3模拟实现apply:

apply与call基本相同,区别在于apply参数的是数组

Function.prototype.apply = function (context, arr) {
    context = context || window;
    context.fn = this;
    var args = [];
    for (var i = 0; i < arr.length; i++) {
        args.push('arr[' + i + ']');
    }
    var result = eval('context.fn(' + args + ')');
    delete context.fn;
    return result;
}

ES6模拟实现apply

Function.prototype,apply = function (context, arr) {
    context = context || window;
    let fn = Symbol();
    context[fn] = this;
    const result = context[fn](...arr);
    Reflect.deleteProperty(context, fn);
    return result;
}

bind和call、apply不同点是:

一、留住this

二、返回一个函数

ES3模拟实现bind

Function.prototype.bind = function (context) {
    if (typeof this !== 'function') {
        throw new Error('bound is not callable')
    }
    // 留住this
    var selt = this;
    // arguments转数组,并从index=1截取
    var args = Array.prototype.slice.call(arguments, 1);
    var fNop = function () {};
    var fBound = function () {
        var boundArgs = Array.prototype.slice.call(arguments);
        return self.apply(this instanceof fNop ? this : context, args.concat(boundArgs));
    }
    // 原型链继承
    fNop.prototype = this.prototype;
    fBound.prototype = new fNop();
    return fBound;
}

ES6模拟实现bind

if (typeof Function.protorype.bind !== 'function') {
    Function.prototype.bind = function (context, ...test) {
        if (typeof this !== 'function') {
            throw new Error('bound is not callable');
        }
        var self = this;
        return function F(...args) {
            if (this instanceof F) {
                return new self(...test, ...args);
            }
            return self.apply(context, test.concat(args));
        }
    }
}

参考链接:

JavaScript实现call、apply和bind





猜你喜欢

转载自blog.csdn.net/qq_42248446/article/details/81009076