按部就班的用js实现一个bind()

若是对call,apply,bind的应用和区别还不了解,能够去看我以前的文章了解下。 让你弄懂 call、apply、bind的应用和区别es6

若是出现错误,请在评论中指出,我也好本身纠正本身的错误bash

author: thomaszhouapp

bind实现

通常咱们会直接使用bind函数,可是此次咱们经过原生js来尝试实现这个函数函数

bind() 方法会建立一个新函数。当这个新函数被调用时,bind() 的第一个参数将做为它运行时的 this,以后的一序列参数将会在传递的实参前传入做为它的参数post

由此咱们能够首先得出 bind 函数的三个特色:优化

  • (1)返回一个函数:咱们可使用 call 或者 apply 实现
  • (2)能够传入参数:咱们用 arguments 进行处理
var foo = { value: 1 };

function bar(name, age) {
    console.log(this.value);
    console.log(name);
    console.log(age);
}

var bindFoo = bar.bind(foo, 'daisy'); // (1)bindFoo是一个函数
bindFoo('18'); // (2)此处能够再次传入参数
// 1
// daisy
// 18
复制代码

先实现前两个特色:实现代码(version 1.0):

Function.prototype.bind2 = function (context) {
    var self = this;
    // 获取bind2函数从第二个参数到最后一个参数
    var args = Array.prototype.slice.call(arguments, 1);
    return function () {
        // 这个时候的arguments是指bind返回的函数bindFoo调用时传入的参数
        var bindArgs = Array.prototype.slice.call(arguments);
        self.apply(context, args.concat(bindArgs));
    }
}
复制代码
  • (3)一个绑定函数也能使用new操做符建立对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。: 经过修改返回的函数的原型来实现
var value = 2;

var foo = {
    value: 1
};

function bar(name, age) {
    this.habit = 'shopping';
    console.log(this.value);
    console.log(name);
    console.log(age);
}

bar.prototype.friend = 'kevin';

var bindFoo = bar.bind(foo, 'daisy');

var obj = new bindFoo('18'); // this 已经指向了 obj
// undefined
// daisy
// 18
console.log(obj.habit);
console.log(obj.friend);
// shopping
// kevin
复制代码

注意:尽管在全局和 foo 中都声明了 value 值,最后依然返回了 undefind,说明绑定的 this 失效了ui

先实现第三个特色:实现代码(version 2.0):

Function.prototype.bind2 = function (context) {
    let self = this;
//    self --> ƒ bar(){}
    let args = Array.prototype.slice.call(arguments, 1);
    let fbound = function () {
      let bindArgs = Array.prototype.slice.call(arguments); 
       // (1) 看成为构造函数时,this --> 实例(fbound建立的的实例),self --> 绑定函数bar,结果为true,那么self指向实例
      // (2) 看成为普通函数时,this -->window,self -->绑定函数,此时结果为false,那么 self指向绑定的 context。
      self.apply(this instanceof self ? this : context, args.concat(bindArgs));
    };
   // 为了要让 this instanceof self为true,就要使建立的实例继承绑定函数bar,
   // 惟一办法就是让建立实例的函数fbound的prototype指向bar函数的prototype
    fbound.prototype = this.prototype;
    return fbound;
};
复制代码

优化:实现代码(version 3.0):

version 2.0 直接将 fbound.prototype = this.prototype,咱们直接修改fbound.prototype 的时候,也会直接修改函数bar的 prototype。这个时候,咱们能够经过一个空函数来进行中转,而后利用组合继承的方式来实现this

Function.prototype.bind2 = function (context) {
    var self = this;
    var args = Array.prototype.slice.call(arguments, 1);
    
    var fNOP = function () {}; // // 定义一个中间函数,用于做为继承的中间值

    var fbound = function () {
        var bindArgs = Array.prototype.slice.call(arguments);
        self.apply(this instanceof self ? this : context, args.concat(bindArgs));
    }
    // 先让 fNOP 的原型方法指向 this 即函数bar的原型方法,继承 this 的属性
    fNOP.prototype = this.prototype;
    // 再将 fbound 即要返回的新函数的原型方法指向 fNOP 的实例化对象
    // 这样,既能让 fBound 继承 this 的属性,在修改其原型链时,又不会影响到 this 的原型链
    fbound.prototype = new fNOP();
    return fbound;
}
复制代码

在上面的代码中,咱们引入了一个新的函数 fun,用于继承原函数的原型,并经过 new 操做符实例化出它的实例对象,供 fBound 的原型继承,至此,咱们既让新函数继承了原函数的全部属性与方法,又保证了不会由于其对原型链的操做影响到原函数spa

-------------优化三点-------------------------

  • (兼容性)当 Function 的原型链上没有 bind 函数时,才加上此函数
Function.prototype.bind = Function.prototype.bind || function () {
    ……
};
复制代码
  • 只有函数才能调用 bind 函数,其余的对象不行。即判断 this 是否为函数。
if (typeof this !== 'function') {
    throw new TypeError("NOT_A_FUNCTION -- this is not callable");
}
复制代码
  • 对于参数传递的代码,能够用ES6的拓展运算符来替换

最终版本!!!

Function.prototype.bind = Function.prototype.bind || function (context, ...formerArgs) {
    let self = this;

    if (typeof this !== 'function') {
			throw new TypeError("NOT_A_FUNCTION -- this is not callable");
	}
    let fNOP = function () {}; 

    let fbound = function (...laterArgs) {
      self.apply(this instanceof self ? this : context, formerArgs.concat(laterArgs)); 
      // es6 : formerArgs.concat(laterArgs)
    };
   
    fNOP.prototype = this.prototype;
    
    fbound.prototype = new fNOP();
    return fbound;
  };

复制代码
相关文章
相关标签/搜索