模拟实现bind方法

发布时间:2019-08-11 发布网站:脚本宝典
脚本宝典收集整理的这篇文章主要介绍了模拟实现bind方法脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。

前言

最近接触到bind方法,为了加深理解,自己模拟实现一下。

首先,谈一下bind()方法的特点:

  1. 把调用bind()的函数(以后统称为初试函数)中的this指向绑定到某个对象上,bind的第二个及其以后的参数作为作为 初始函数的 参数,bind()执行完返回一个新的函数。
  2. 新返回的函数如果用 new 字符来调用的话,那么之前this指向绑定到某个对象上 将会失效,并且初始函数中的this指向绑定到 new调用的 函数实例上。函数实例上还可以调用初始函数的原型上的方法

那么先实现第一个特点


// 因为需要所有函数都能执行,所以绑定到Function.PRototyPE
Function.prototype.binDFn=function(thisarg) {
   if(typeof this !== 'function') {
         throw new TypeError(this+' is not a function');
   }
   
   // 调用bindFn方法的函数的引用
   VAR self = this;
   // 以数组形式保存第二个及其以后的参数
   var beforeArg = [].slice.call(arguments,1);

   var bound = function() {
        // 以数组形式保存着当前函数的所有参数
        var afterArg = [].slice.call(arguments);
        // bindFn第二个及其以后的参数和当前函数所有参数的 集合
        // 全部传到self函数的参数里
        var finalArgs = beforeArg.concat(afterArg);
        // self中的this指向绑定到 thisArg
        return  self.apply(thisArg, finalArgs);
   }
   return bound;
}
var obj1={sex:23};

function m1(a,b) {
    console.LOG(a+b); // 6
    console.log(this); // {sex:23}
}

var bound=m1.bindFn(obj1,2);
var result=bound(4); 

可见m1中的this指向了obj1对象,2,4分别传给了形参a,b。

进一步完善bindFn,实现第二个特点

// 因为需要所有函数都能执行,所以绑定到Function.prototype上
Function.prototype.bindFn=function(thisArg) {
   if(typeof this !== 'function') {
         throw new TypeError(this +' is not a function');
   }
   
   // 调用bindFn方法的函数的引用
   var self = this;
   // 以数组形式保存第二个及其以后的参数
   var beforeArg = [].slice.call(arguments,1);

   var bound = function() {
        // 以数组形式保存着当前函数的所有参数
        var afterArg = [].slice.call(arguments);
        // bindFn第二个及其以后的参数和当前函数所有参数的 集合
        // 全部传到self函数的参数里
        var finalArgs = beforeArg.concat(afterArg);
        // 如果new调用的话,this指向实例对象
        // 否则this指向需要绑定的对象
        // this instanceof bound并不准确,可以用es6中的new.target来解决
        return  self.apply(this instanceof bound ? this : thisArg, finalArgs);
   }
   
   // new调用的时候有用
   // 避免es6中的箭头函数
   // 箭头函数没有prototype
   if(this.prototype) {
         // 避免修改 bound.prototype 污染到 this.prototype
      function a(){}
      a.prototype=this.prototype;
      bound.prototype=new a();
      bound.prototype.constructor=bound;
   }
   return bound;
}
var obj1={sex:23};

function m1(a,b) {
    this.name='m1';
    console.log(a+b); // 6
    console.log(this); // {name:'m1'}
}

m1.prototype.allKey=function() {
    console.log('this is allkey');
}

var bound=m1.bindFn(obj1,2);
var result=new bound(4); 
result.allKey(); // this is allkey

模拟实现bind方法

可见,m1中的this指向了result这个实例对象,并且result 通过原型链 继承了m1.prototype上的方法。

那么我们看下官方bind的使用:

// 关键部分
function m1(a,b) {
    this.name='m1';
    console.log(a+b); // 6
    console.log(this); // {name:'m1'}
}
var bound=m1.bind(obj1,2);
var result=new bound(4); 
result.allKey(); // this is allkey

模拟实现bind方法

由上图可见,虽然原型链上有所不同,但我们模拟的方法还是实现了bind的功能。我们模拟的方法bindFn参考mdn上的Polyfill。基本上关于模拟bind的实现就是这样。

现在我们在延伸一下关于原型链的知识,如果刚才的bound.prototype完全重写在了bound构造函数内,又会怎样

// 因为需要所有函数都能执行,所以绑定到Function.prototype上
Function.prototype.bindFn=function(thisArg) {
   if(typeof this !== 'function') {
         throw new TypeError(this +': is not a function');
   }
   
   // 调用bindFn方法的函数的引用
   var self = this;
   // 以数组形式保存第二个及其以后的参数
   var beforeArg = [].slice.call(arguments,1);

   var bound = function() {
        // 以数组形式保存着当前函数的所有参数
        var afterArg = [].slice.call(arguments);
        // bindFn第二个及其以后的参数和当前函数所有参数的 集合
        // 全部传到self函数的参数里
        var finalArgs = beforeArg.concat(afterArg);
        // 用new 方式调用
        if(this instanceof bound) {
             // 避免es6箭头函数
             // 箭头函数没有prototype
         if(self.prototype) {
                 // 避免修改 bound.prototype 污染到 self.prototype
              function a(){}
              a.prototype=self.prototype;
              bound.prototype=new a();
              bound.prototype.constructor=bound;
            
         }
         // self中的this指向 new生成的实例对象
            return self.apply(this,finalArgs);
        }
        // 无new 调用方式, self中的this指向thisArg
        return  self.apply(thisArg, finalArgs);
   }
     bound.prototype.getKey=function() {

              }
   return bound;
};
var obj1={sex:23};

function m1(a,b) {
    this.name='m1';
    console.log(a+b); // 6
    console.log(this);  
}

m1.prototype.allKey=function() {
    console.log('this is allkey');
}

var bound=m1.bindFn(obj1,2);
var result=new bound(4); 
result.allKey(); // 报错 result.allKey is not a function

按理说 result原型链上应该allKey方法的,结果却报错了,我们把m1中的this打印一下看看

模拟实现bind方法

这里我们看到原型链上并没有m1.prototype, 所以在构造函数内部 完全重写 构造函数原型对象的话,原型链会与预想的不同。

那么图上的bound.prototype打个 ? ,因为并不确定这个是bound.prototype,如果真是bound.prototype的话,那么下面的话如何解释?

console.log(result.__proto__ === bound.prototype ) // false

按理说 result.__proto__ 指向的是bound.prototype,结果却为false。所以不能肯定刚才的就是bound.prototype

总结: 完全重写构造函数原型对象 要放在构造函数后面,如果放在构造函数里面,可能原型链会有错误。

脚本宝典总结

以上是脚本宝典为你收集整理的模拟实现bind方法全部内容,希望文章能够帮你解决模拟实现bind方法所遇到的问题。

如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典推荐好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。