前端基础

apply、call、bind模拟实现

2019-02-19  本文已影响0人  Luigi_Lin

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");
}
上一篇下一篇

猜你喜欢

热点阅读