美文网首页
深入之bing的模拟实现

深入之bing的模拟实现

作者: 明里人 | 来源:发表于2019-07-12 17:24 被阅读0次

一句话介绍bind:

bind()方法会创建一个新函数。当这个新函数被调用时,bind()的第一参数将作为它允许时的this,之后的一序列参数将会在传递的实参前传入作为它的参数。(来自于MDN)

bind 函数的两个特点:
1、返回一个函数
2、可以传入参数

返回函数的模拟实现
// 第一版
Function.prototype.bind2 = function(context){
  var self = this;
  return function(){
    return self.apply(context);
  }
}

之所以 return self.apply(context),是考虑到绑定函数会有返回值,如:

let foo = {
  value: 1
}
function bar(){
  return this.value;
}
let bindFoo = bar.bind(foo);
console.log(bindFoo()); // 1
传参模拟实现

在使用bind时是否可以传参呢?执行bind返回的函数时是否可以传参?

let foo = {
  value: 1
}
function bar(name,age){
  console.log(this.value);
  console.log(name);
  console.log(age);
}
let bindFoo = bar.bind(foo,'cyl');
bindFoo('18');
// 1
// cyl
// 18

函数可以在bind的时候和执行返回的函数进行传参

// 第二版
Function.prototype.bind2 = function(context){
  let self = this;
  // 获取bind2函数 从第二个参数到最后一个参数
  let args = Array.prototype.slice.call(arguments,1);

  return function (){
    //这里函数的arguments是指bind返回的函数入参
    let bindArgs = Array.prototype.slice.call(arguments);
    return self.apply(context, args.concat(bindArgs));
  }
}
构造函数模拟实现

一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的this值被忽略,同时调用的参数被提供给模拟函数

当bind返回的函数作为构造函数时,bind时指定的this值会失效,但传入的参数依然生效。

let value = 2;
let foo = {
    value: 1
}
function bar(name,age){
    this.a = '12';
    console.log(this.value);
    console.log(name);
    console.log(age);
}
bar.prototype.friend = 'cegz';
let bindFoo = bar.bind(foo,'cyl');

// new bindFoo时执行了bindFoo,但new将this指向了实例对象obj,
// 而obj并没有value这个属性,所以不会访问到bar和全局的value
let obj = new bindFoo('18');  
// undefined
// cegz
// 18
console.log(obj.friend);  // cegz
console.log(obj.a);  // 12

要实现上面的功能,我们可以通过修改返回的函数的原型来实现

//第三版
Function.prototype.bind2 = function(context){
  let self = this;
  let args = Array.prototype.slice.call(arguments,1); 
  
  let fBound = function(){
    let bindArgs = [... arguments];
    //如果当前this为fBound的实例,表示是执行了new,指向this,否则指向bind对象
    return self.apply(this instanceof fBound ? this : context, args.concat(bindArgs));
  }
  //修改返回函数的 prototype 为绑定函数的 prototype,new出实例对象就可以继承绑定函数的原型中的成员
  fBound.prototype = this.prototype;
  return fBound;
}
构造函数效果的优化实现

如果我们直接将fBound.prototype = this.prototype,我们直接修改 fBound.prototype的时候,也会直接修改绑定函数的prototype,这时候我们通过一个空函数进行中转

// 第四版
Function.prototype.bind2 = function(context){
  let self = this; 
  let args = Array.prototype.slice.call(arguments,1);
  
  let fNOP = function(){};
  let fBound = function(){
    let bindArgs = Array.prototype.slice.call(arguments);
    return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
  }
  fNOP.prototype = this.prototype;
  fBound.prototype = new fNOP();
  return fBound;
}

最终代码

Function.prototype.bind2 = function(context){
  if(typeof this !== 'function'){
    throw new Error('Function.prototype.bind - what is trying to be bound is not callable');
  }

  let self = this;
  let args = Array.prototype.slice.call(arguments,1);

  let fNOP = function(){}
  let fBound = function(){
    let bindArgs = Array.prototype.slice.call(arguments);
    return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
  }

  fNOP.prototype = this.prototype;
  fBound.prototype = new fNOP();
  return fBound;
}

相关文章

网友评论

      本文标题:深入之bing的模拟实现

      本文链接:https://www.haomeiwen.com/subject/zttykctx.html