apply、call、bind模擬實現

call

call() 方法調用一個函數, 其具有一個指定的this值和分別地提供的參數(參數的列表)。

例如:

    var foo = { x: 10 };

    function test(y) {
        console.log(this.x,y);
    }

    test.call(foo,20); //10 20
  1. 為達到綁定this的效果,可以將函數加到傳入的對象上進行調用。
    Function.prototype.call2 = function (self) {
        self.fn = this;
        return self.fn();
    }

但這樣傳入的對象就多了一個屬性了

  1. 在調用后刪除掉這個屬性
    Function.prototype.call2 = function (self) {
        self.fn = this;
        var result = self.fn();
        delete self.fn();
        return result;
    }
  1. 將提供的參數傳入
    Function.prototype.call2 = function (self) {        
        self.fn = this;

        var params = [];
        for (var i = 1; i < arguments.length; i++) {
            params.push(`arguments[` + i + `]`);
        }

        var result = eval('self.fn(' + params + ')');
        delete self.fn;
        return result;
    }
  1. 當傳入對象為空時,this綁定到window
    Function.prototype.call2 = function (self) {
        self = self || window;
        self.fn = this;

        var params = [];
        for (var i = 1; i < arguments.length; i++) {
            params.push(`arguments[` + i + `]`);
        }

        var result = eval('self.fn(' + params + ')');
        delete self.fn;
        return result;
    }

apply

與call類似,區別時apply傳入的參數列表為數組

    Function.prototype.apply2 = function (self, args) {
        self = self || window;

        self.fn = this;

        var result;
        if (!args || !args.length) {
            result = self.fn();
        } else {
            var params = [];
            for (var i = 0; i < args.length; i++) {
                params.push(`args[` + i + `]`);
            }
            result = eval('self.fn(' + params + ')');

        }
        delete self.fn;

        return result;
    }

bind

  1. bind會返回一個函數,該函數的this綁定到第一個參數上。
  Function.prototype.bind2 = function (self) {
      var fn = this;

      return function () {
          return fn.apply(self);
      }
  }
  1. bind返回的函數可以接收參數
    Function.prototype.bind2 = function (self) {
        var fn = this;

        return function () {
            var bindArgs = Array.prototype.slice.call(arguments);
            return fn.apply(self,bindArgs);
        }
    }
  1. 調用bind的時候可以先綁定一部分參數,在調用其返回的函數時傳入的參數會跟在bind綁定的那部分參數后面
    Function.prototype.bind2 = function (self) {
        var fn = this;
        var args = Array.prototype.slice.call(arguments, 1);

        return function () {
            var bindArgs = Array.prototype.slice.call(arguments);
            return fn.apply(self, args.concat(bindArgs));
        }
    }
  1. 對bind返回的新函數進行構造調用時,this綁定到新構造的對象上,而非bind綁定的對象。
    Function.prototype.bind2 = function (self) {
        var fn = this;
        var args = Array.prototype.slice.call(arguments, 1);

        var fBound = function () {
            var bindArgs = Array.prototype.slice.call(arguments);
            //如果this是此函數的實例,證明是被構造調用了,此時綁定新構造的對象
            return fn.apply( this instanceof fBound ? this : self, args.concat(bindArgs));
        }

        return fBound;
    }
  1. 維護原型關系,否則會有這樣的問題
  function Test(){}
  var t = Test.bind({});
  var o = new t();
  o instanceof Test // false
    Function.prototype.bind2 = function (self) {
        var fn = this;
        var args = Array.prototype.slice.call(arguments, 1);

        var f = function(){};
        var fBound = function () {
            var bindArgs = Array.prototype.slice.call(arguments);
            return fn.apply(this instanceof fBound ? this : self, args.concat(bindArgs));
        }

        if(this.prototype){
            f.prototype = fn.prototype;
        }
        fBound.prototype = new f();

        return fBound;
    }

6.調用bind的不是函數要報錯

if (typeof this !== "function") {//加上判斷
  throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容