1. 程式人生 > >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));
        }
    }
}

參考連結: