【js基础修炼之路】- 手把手教你实现bind

手写bind前我们先回顾一下bind有哪些特性,以便更好的理解bind和实现bind。

bind的特性

var obj = {
    a: 100,
    say(one, two) {
        console.log(this.a, one, two);
    }
}
var obj2 = {
    a: 300
}
var res = obj.say.bind(obj2, 1, 2);
res();
//300 1 2

可以看出:

  • bind是函数的方法,只有函数可以调用

  • bind的第一个参数是this指向,剩下的参数作为调用者的参数

  • bind方法返回的是一个函数,需要再次调用才能执行

function test(){
    this.a = 10,
    this.b = 20
};
var foo = {
    a:200
}
var res = test.bind(foo);
var res2 = new res();
console.log(res2);
//test {a: 10, b: 20}

从上面可以看出,new之后this执行不跟随bind的第一个参数了,知道new是怎么实现的小伙伴一定知道为什么(不知道的可以看一下这篇文章的末尾 https://www.jianshu.com/p/a6068c1296f3)此时的this指向了res2。
知道了bind的特性,下面我们来实现一下bind

手把手教你实现bind

我们知道bind返回的是一个函数,调用者也是一个函数,并且bind改变了this指向,而且bind还可以传参,下面我们来实现一下这些功能:

 Function.prototype.bind = function(oThis) {
    // 判断调用者是不是函数
    if(typeof this != 'function'){
          throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
     }
     //保存this(this指向调用bind者)
     var fToBind = this;
     //获取传入bind函数的第二个及其后面的参数(除去this参数)
     var aArgs = Array.prototype.slice.call(arguments,1);
     var fBound = function(){
        //this instanceof fBound === true时,说明返回的fBound被当做new的构造函数调用
        //== false的时候说明当做了普通函数来调用,this为bind的第一个参数
        return  fToBind.apply(this instanceof fBound ? this : oThis,aArgs.concat(Array.prototype.slice.call(arguments)));
     }
     // 返回新函数
     return fBound;
 }

上面的代码实现了一个基本的bind,但是还有一些问题,例如上面只是绑定了this,但是原函数的原型新函数并没有继承,所以我们需要再次继承一下原型:

 Function.prototype.bind = function(oThis) {
    // 判断调用者是不是函数
    if(typeof this != 'function'){
          throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
     }
     //保存this(this指向调用bind者)
     var fToBind = this;
     //获取传入bind函数的第二个及其后面的参数(除去this参数)
     var aArgs = Array.prototype.slice.call(arguments,1);
     var fBound = function(){
        //this instanceof fBound === true时,说明返回的fBound被当做new的构造函数调用
        //== false的时候说明当做了普通函数来调用,this为bind的第一个参数
        return  fToBind.apply(this instanceof fBound ? this : oThis,aArgs.concat(Array.prototype.slice.call(arguments)));
     }
      //绑定原型
      fBound.prototype = this.prototype;
     // 返回新函数
     return fBound;
 }

本以为大功告成,但是还有一个问题,看下面的例子:

 function bar() {}
 var bindFoo = bar.bind(null);
 bindFoo.prototype.value = 1;
 console.log(bar.prototype.value) // 1

我只改变了bindFoo的原型,bar的为什么也跟着变了,因为在写bind的时候把bar的原型赋给了bindFoo,所以导致了这种情况,下面我们用一个中转的函数来解决这个问题:

 Function.prototype.bind = function(oThis) {
    // 判断调用者是不是函数
    if(typeof this != 'function'){
          throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
     }
     //保存this(this指向调用bind者)
     var fToBind = this;
     //获取传入bind函数的第二个及其后面的参数(除去this参数)
     var aArgs = Array.prototype.slice.call(arguments,1);
     var fNOP = function() {};
     var fBound = function(){
        //this instanceof fBound === true时,说明返回的fBound被当做new的构造函数调用
        //== false的时候说明当做了普通函数来调用,this为bind的第一个参数
        return  fToBind.apply(this instanceof fBound ? this : oThis,aArgs.concat(Array.prototype.slice.call(arguments)));
     }
     // 为了让 fBound 构造的实例能够继承绑定函数的原型中的值
     if (this.prototype) {
         fNOP.prototype = this.prototype;
     }
     // 下行的代码使fBound.prototype是fNOP的实例,因此
     // 返回的fBound若作为new的构造函数,new生成的新对象作为this传入fBound,新对象的__proto__就是fNOP的实例
     fBound.prototype = new fNOP();
     // 返回新函数
     return fBound;
 }

对于代码有疑问的小伙伴可以留言或者看注释!!!

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 第3章 基本概念 3.1 语法 3.2 关键字和保留字 3.3 变量 3.4 数据类型 5种简单数据类型:Unde...
    RickCole阅读 5,192评论 0 21
  • 概要 64学时 3.5学分 章节安排 电子商务网站概况 HTML5+CSS3 JavaScript Node 电子...
    阿啊阿吖丁阅读 9,362评论 0 3
  • 函数和对象 1、函数 1.1 函数概述 函数对于任何一门语言来说都是核心的概念。通过函数可以封装任意多条语句,而且...
    道无虚阅读 4,673评论 0 5
  • title: js面向对象date: 2017年8月17日 18:58:05updated: 2017年8月27日...
    lu900618阅读 582评论 0 2
  • 文/弭路 她的面容负上一朵洁白的云 在青天白日胡乱闯荡 张扬一片风尘的欣喜 如同英雄举杯豪饮 夜色挂不住她肩上的衣...
    弭路阅读 261评论 0 0