前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >重写手动实现bind函数

重写手动实现bind函数

作者头像
挥刀北上
发布2019-07-19 15:44:51
1.4K0
发布2019-07-19 15:44:51
举报
文章被收录于专栏:Node.js开发Node.js开发

前面发了一篇文章,面试题目之原生实现call、apply、bind,这篇文章介绍了如何手动实现call、apply、bind,但是前不久重读这篇文章时发现了实现bind的代码不是很完善,我们看一段代码:

代码语言:javascript
复制
function Person(){
  this.name="zs";
  this.age=18;
  this.gender="男"
}
var obj={
  hobby:"看书"
}
//  将构造函数的this绑定为obj
var changePerson = Person.bind(obj);
//  直接调用构造函数,函数会操作obj对象,给其添加三个属性;
changePerson();
//  1、输出obj
console.log(obj);

//  用改变了this指向的构造函数,new一个实例出来
var p = new changePerson();
// 2、输出obj
console.log(p);

代码输出结果:

1、输出:

2、输出:

仔细观察上面的代码,再看输出结果。

我们对Person类使用了bind将其this指向obj,得到了changeperson函数,此处如果我们直接调用changeperson会改变obj,若用new调用changeperson会得到实例 p,并且其__proto__指向Person,我们发现bind失效了。

我们得到结论:用bind改变了this指向的函数,如果用new操作符来调用,bind将会失效。

再看我们这篇文章(面试题目之原生实现call、apply、bind)中bind实现的代码:

代码语言:javascript
复制

Function.prototype.myBind = function(ctx, ...argv1) {
    return (...argv2) => {
        return this.call(ctx, ...argv1, ...argv2)
    }
}

如果看不太习惯,将其转化为es5的执行方式:

代码语言:javascript
复制
Function.prototype.mybind = function(){
  // 1、保存函数
  var _this = this;
  // 2、保存目标对象
  var context = arguments[0]||window;
  // 3、保存目标对象之外的参数,将其转化为数组;
  var rest = Array.prototype.slice.call(arguments,1);
  // 4、返回一个待执行的函数
  return function F(){
  // 5、将二次传递的参数转化为数组;
    var rest2 = Array.prototype.slice.call(arguments)
    //6、用apply调用第一步保存的函数,并绑定this,传递合并的参数数组
    _this.apply(context,rest.concat(rest2));
  }
}

我们用自己实现的mybind函数,来实现文章最上面的例子,测试一下如果,用mybind函数改变了构造函数的this,然后用new来执行生成的新函数,能否得到和原生bind一样的效果,测试代码如下:

代码语言:javascript
复制
function Person(){
  this.name="zs";
  this.age=18;
  this.gender="男"
}
var obj={
  hobby:"看书"
}
//  将构造函数的this绑定为obj ,此处调用上面开发的mybind方法;
var changePerson = Person.mybind(obj);
//  直接调用构造函数,函数会操作obj对象,给其添加三个属性;
changePerson();
//  1、输出obj
console.log(obj);

//  用改变了this指向的构造函数,new一个实例出来
var p = new changePerson();
// 2、输出obj
console.log(p);

查看输出结果:

1、输出:

2、输出:

我们用上面实现的mybind改变函数的this,然后调用new方法,发现并未实现和原生bind一样的效果,我们实现的mybind方法和原生的bind实现的功能还有些差距,那么我们如何修正呢?

仔细观察代码,发现突破点再这里: new changeperson()。

这里我们只需要在调用 new changeperson()时候,判断一下,是否是通过new操作符调用的,如果是new 操作符调用的话,我们就用new直接调用未改变this之前的函数,并返回其结果。

那么如何判断是否是通过new操作符来调用一个函数呢?这里我们就要用到instanceof了,看看官方文档对其解释:


| instanceof运算符用于测试构造函数的prototype属性是否出现在对象的

| 原型链中的任何位置。

翻译成大白话,就是判断某个实例是否由某个类或者构造函数生成。

回归正文,我们知道,我们在用new操作符调用一个构造函数时,或者普通函数,都会在函数内部执行如下步骤:

1、生成一个空对象,

2、然后将this指向这个空对象,

3、最后将这个对象返回。

而这个对象就是这个构造函数的实例,那么只要在函数内部执行 this instanceof 构造函数 来判断其结果是否为true,就能判断函数是否是通过new操作符来调用了,若结果为true则是用new操作符调用的,代码修正如下:

代码语言:javascript
复制
Function.prototype.mybind = function(){
  // 1、保存函数
  var _this = this;
  // 2、保存目标对象
  var context = arguments[0]||window;
  // 3、保存目标对象之外的参数,将其转化为数组;
  var rest = Array.prototype.slice.call(arguments,1);
  // 4、返回一个待执行的函数
  return function F(){
     // 5、将二次传递的参数转化为数组;
     var rest2 = Array.prototype.slice.call(arguments)
    if(this instanceof F){
      // 6、若是用new操作符调用,则直接用new 调用原函数,并用扩展运算符传递参数
      return new _this(...rest2)
    }else{
     
      //7、用apply调用第一步保存的函数,并绑定this,传递合并的参数数组
      _this.apply(context,rest.concat(rest2));
    }
  }
}

此时,测试在运行上面的测试案例,打印结果为:

完美实现了和原生bind一样的效果,对一个知识点进行比较深入的研究确实不容易,越深入发现涉及的知识越广泛,就像这篇文章,虽然说得是bind的手动实现,但是其实涉及了new操作符调用的原理,instanceof 的用法。

关于call、apply、bind算上这篇文章,笔者一共写了三篇,分别是:

call、apply和bind的区别

面试题目之原生实现call、apply、bind

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-04-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 nodejs全栈开发 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档