bind(),apply(),call()理解

159 阅读3分钟

Function.prototype.call()

// call函数的签名
fun.call(thisArg, arg1, arg2, ...)

call函数可以这样理解

  • 允许为不同的对象分配和调用属于一个对象的函数/方法
  • 提供新的 this 值给当前调用的函数/方法。

参数:

  • thisArg: 在 fun 函数运行时指定的 this 值。
  • arg1..:指定的参数列表。

call函数把某个方法的this改成了第一个参数。给该函数传入多个参数,并执行当前函数。

call函数有这么几个要点,需要理解:

  • this可以传null,可以传空,这时候this指向window
  • 函数是可以有返回值的!有返回值返回返回值,没有返回值返回undefined
    /*
    	把函数当成对象,然后删除对应的属性值
    */
    Function.prototype.call2 = function (context) {
    	// 判断当前的this对象是否为null,为空指向window
      var context = context || window;
    	// 首先要获取调用call的函数,用this可以获取
      context.fn = this;
    
      var args = [];
    	// 获取call的参数
      for(var i = 1, len = arguments.length; i < len; i++) {
          args.push('arguments[' + i + ']');
      }
    	// eval执行该函数
      var result = eval('context.fn(' + args +')');
    	// 删掉调用对象的这个属性
      delete context.fn
      return result;
    }

Function.prototype.apply()

// apply的函数签名
func.apply(thisArg, [argsArray])

apply函数和call函数实现的是相同的功能

  • 允许为不同的对象分配和调用属于一个对象的函数/方法
  • 提供新的 this 值给当前调用的函数/方法。

apply函数的参数:

  • thisArg: 在 fun 函数运行时指定的 this 值。
  • argsArray:指定的参数数组。

实现:

    Function.prototype.apply = function (context, arr) {
       // 判断当前的this对象是否为null,为空指向window
      var context = context || window;
    	// 首先要获取调用call的函数,用this可以获取
      context.fn = this;
    
      var result;
      if (!arr) {
          result = context.fn();
      }
      else {
          var args = [];
          for (var i = 0, len = arr.length; i < len; i++) {
              args.push('arr[' + i + ']');
          }
          result = eval('context.fn(' + args + ')')
      }
    
      delete context.fn
      return result;
    }

Function.prototype.bind()

// bind函数的函数签名
function.bind(thisArg[, arg1[, arg2[, ...]]])

bind()方法创建一个新的函数,在bind()被调用时,这个新函数的this被bind的第一个参数指定,其余的参数将作为新函数的参数供调用时使用。

参数:

  • thisArg 在 fun 函数运行时指定的 this 值。
  • arg1..:指定的参数列表。

bind() 函数生成的新函数,不能自己执行,需要手动执行一下。但是这个新函数的this永远都是bind的第一个参数。

因为 bind 还有一个特点,就是

一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。

也就是说当 bind 返回的函数作为构造函数的时候,bind 时指定的 this 值会失效,但传入的参数依然生效

实现

    Function.prototype.bind2 = function (context) {
    	// 调用bind的是否是函数做一个判断
      if (typeof this !== "function") {
        throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
      }
    	// 把调用者和要传给函数的参数保存一下。
      var self = this;
    	// 获取bind2函数从第二个参数到最后一个参数
      var args = Array.prototype.slice.call(arguments, 1);
    	// 创建一个空函数来中转
      var fNOP = function () {};
    
      var fBound = function () {
          var bindArgs = Array.prototype.slice.call(arguments);
    			// 当作为构造函数时,this 指向实例,此时结果为 true,将绑定函数的 this 指向该实例,可以让实例获得来自绑定函数的值
          // 以上面的是 demo 为例,如果改成 `this instanceof fBound ? null : context`,实例只是一个空对象,将 null 改成 this ,实例会具有 habit 属性
          // 当作为普通函数时,this 指向 window,此时结果为 false,将绑定函数的 this 指向 context
          return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
      }
    	// 修改返回函数的 prototype 为绑定函数的 prototype,实例就可以继承绑定函数的原型中的值
      fNOP.prototype = this.prototype;
      fBound.prototype = new fNOP();
      return fBound;
    }