JavaScript中this的绑定规则call、apply、bind方法的实现

280 阅读3分钟

First

在JavaScript中,有四种this绑定规则分别是:

  • 默认绑定(window)
  • 隐式绑定(调用对象)
  • 显示绑定(call、apply、bind)
  • new 绑定

这篇文章主要实现一下显示绑定规则的三种方式;

一、call(thisArg, arg1, arg2, ...)方法

call()方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数;

                                                //用在这里的...表示剩余参数运算符 = [arg1, arg2...]
  Function.prototype.mycall = function(thisArg, ...args) {
      if (typeof this !== "function") { 
          throw new Error("Type error"); 
      };
      // 1.获取需要被执行的函数,也就是我们的调用者foo、baz...也就是this;
      var fns = this;
      
      // 2.对绑定的this做边界判断;
      // 为什么使用Object()呢?对转入字符串和数字的处理,转成对象String{}、Number{};
      thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window;
      
      // 3.使用绑定的this调用需要被执行的函数。第二步调用第一步;
      thisArg.fns = fns;
                              //用在这里的...表示展开运算符 = (arg1, arg2...)
      var result = thisArg.fns(...args);
      // 4.如果函数有返回值,拿到返回结果并返回出去;
      return result;
  };
  
  function foo() {
      // 为什么多了个fns:f呢?因为我们往thisArg添加了属性,调用完之后并没有删除;
      // 可以在调用完thisArg.fns(); 之后执行 delete thisArg.fns;
      // 打印这些内容是在fns调用时就打印了,所以删除之后,这里的打印依旧会有fns:f属性;实际上已经删除了;
      console.log("foo函数", this); //5. 输出 `foo函数 {name: 'foo', fns: ƒ}`
  };
  
  function baz(n1, n2) {
      console.log("baz函数", this, n1, n2); // 6.输出 `baz函数 {name: 'baz', fns: ƒ} 10 20`
                                                     
      return n1 + n2;
  };
  
  // 调用我们在函数的显示原型上面添加的方法`mycall()`;
  foo.mycall({name: 'foo'});
  
  var res = baz.mycall({name: 'baz'}, 10, 20);
  console.log(res); //30 有返回值,就接收使用。

二、apply(thisArg, [argsArray])方法

该方法的语法和作用与call()方法类似,只有一个区别,就是 call() 方法接受的是一个参数列表,而 apply() 方法接受的是一个包含多个参数的数组;

   Function.prototype.myapply = function(thisArg, argsArray) {
       if (typeof this !== "function") { 
          throw new Error("Type error"); 
       };
       var fns = this;
       
       thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window;
       argsArray = argsArray || []; //未传入参数就是空数组;
       thisArg.fns = fns;
       
       var result = thisArg.fns(...argsArray);
       return result;
   };
   
   function foo() {
       console.log('foo函数', this); // 输出 `foo函数 {name: 'foo', fns: ƒ}`
   };
   
   function baz(n1, n2) {
       console.log('baz函数', this, n1, n2); // 输出 `baz函数 {name: 'baz', fns: ƒ} 10 20`
       
       return n1 + n2;
   };
   
   foo.myapply({name: 'foo'});
   var res = baz.myapply({name: 'baz'}, 10, 20);
   console.log(res); //30
   
   

三、bind(thisArg, arg1, arg2...)方法

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

   Function.prototype.mybind = function(thisArg, ...args) {
       if (typeof this !== "function") { 
          throw new Error("Type error"); 
       };
       var fns = this;
       
       thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window;
       thisArg.fns = fns;
       
       return function(...args2) {
           // 参数合并;
           var finalArgs = [...args, ...args2];
           
           var result = thisArg.fns(...finalArgs);
           return result;
       };
   };
   
   function foo() {
       console.log('foo函数', this); //输出 `foo函数 {name: 'foo', fns: ƒ}`
   };
   
   function baz(n1, n2, n3, n4) {
       console.log('baz函数', this, n1, n2, n3, n4); //输出 `baz函数 {name: 'baz', fns: ƒ} 10 20 30 40`
   };
   
   var fn1 = foo.mybind({name: 'foo'});
   fn1();
   
   var fn2 = baz.mybind({name: 'baz'}, 10, 20);
   fn2(30, 40);

Finish

实现这三个方法并没有过多的考虑一些边界情况,不足的地方还希望多多指点,谢谢!

1)默认规则的优先级最低;

2)显示绑定优先级高于隐式绑定;

3)new 绑定优先级高于隐式绑定;

4)new 绑定优先级高于bind;new绑定和call、apply是不允许同时使用的;

不是因为没有信念而失败,而是因为不能把信念化成动力,并且坚持到底。