JS_bind

作者: anddju | 来源:发表于2019-06-21 16:20 被阅读0次

前言

bind、call、apply 三个改变指向函数,在JS面试中,经常问道。
本文收录了 四种 手写bind的方案

区别

bind 和 call/apply 有一个很重要的区别,一个函数被 call/apply 的时候,会直接调用,但是 bind 会创建一个新函数
当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this,之后的一序列参数将会在传递的实参前传入作为它的参数。

MDN

bind 做了什么

bind() 函数会创建一个 新绑定函数(bound function,BF)
绑定函数是一个exotic function object(怪异函数对象,ECMAScript 2015中的术语),它包装了原函数对象。调用绑定函数通常会导致执行包装函数。
绑定函数具有以下内部属性:

  1. [[BoundTargetFunction]] - 包装的函数对象
  2. [[BoundThis]] - 在调用包装函数时始终作为this值传递的值。
  3. [[BoundArguments]] - 列表,在对包装函数做任何调用都会优先用列表元素填充参数列表。
  4. [[Call]] - 执行与此对象关联的代码。通过函数调用表达式调用。内部方法的参数是一个this值和一个包含通过调用表达式传递给函数的参数的列表。

当调用绑定函数时,它调用[[BoundTargetFunction]]上的内部方法[[Call]],就像这样Call(boundThis, args)。其中,boundThis是[[BoundThis]],args是[[BoundArguments]]加上通过函数调用传入的参数列表。

绑定函数也可以使用new运算符构造,它会表现为目标函数已经被构建完毕了似的。提供的this值会被忽略,但前置参数仍会提供给模拟函数。

手写函数

最佳手写方案:https://github.com/Raynos/function-bind/blob/master/implementation.js

// 方法1
Function.prototype.my_bind = function(context) {
    if(typeof this !== "function"){
        throw new TypeError("not a function");
    }
    let self = this;
    let args = [...arguments].slice(1);
    function Fn() {};
    Fn.prototype = this.prototype;
    let bound = function() {
        let res = [...args, ...arguments]; //bind传递的参数和函数调用时传递的参数拼接
        context = this instanceof Fn ? this : context || this;
        return self.apply(context, res);
    }
    //原型链
    bound.prototype = new Fn();
    return bound;
}
/**
作者:刘小夕
链接:http://www.imooc.com/article/286880
来源:慕课网
本文原创发布于慕课网 ,转载请注明出处,谢谢合作 **/

//方法2
/*
函数的 bind 方法核心是利用 call,同时考虑了一些其他情况,例如
  bind 返回的函数被 new 调用作为构造函数时,绑定的值会失效并且改为 new 指定的对象
  定义了绑定后函数的 length 属性和 name 属性(不可枚举属性)
  绑定后函数的原型需指向原来的函数
*/
const isComplexDataType = obj => (typeof obj === 'object' || typeof obj === 'function') && obj !== null

const selfBind = function(bindTarget , ...arg1){
  if(typeof this !== 'function') throw new TypeError("bind must be called on a function");
  let func = this;
  let boundFunc = function(...arg2){
    le targs = [...arg1,...arg2];
    if(new.target){
      let res = func.apply(this,args);
      if(isComplexDataType(res)) return res;
      return this;
    } else {
      func.apply(bindTarget , args)
    }
  }

  this.prototype && (boundFunc.prototype == Object.create(this.prototype));

  let desc = Object.getOwnPropertyDescriptors(func);
  Object.defineProperties( boundFunc , {
    length : desc.length,
    name : Object.assign( desc.name , {
      value : `bound${desc.name.value}`
    })
  });
}

// 方法3
Function.prototype.newBing = function(target){
    //target改变返回函数执行的this指向
    var self = this;//谁调用,this指向谁,此处将self=调用newBing的函数
    var args = [].slice.call(arguments,1);//此处见他人博客  cichuhttps://blog.csdn.net/u011402896/article/details/79546268
    var temp = function(){};  //缓冲函数(圣杯模式)
    var f = function(){
        var _arg = [].slice.call(arguments,0); //此处是函数B执行时的参数
        //真正执行的是调用newBing的函数
        return self.apply(this instanceof temp ? this : (target || window),args.concat(_arg));  //apply将方法内部的this指向改变为第一个参数,详细见 原型链 call/apply 继承模式   http://note.youdao.com/noteshare?id=7d5b99baf295b3499a49681cfdf72837
    // instanceof  是表示this能否在原型链上找到自己的原型temp,实现5的功能
    }
    temp.prototype = self.prototype;
    f.prototype = new temp();     //建立继承关系,圣杯模式    以f为构造函数去创建出的对象,能找到self.  实现5的功能。
    return f;
}
/** ---------------------
作者:菜鸡的全栈路
来源:CSDN
原文:https://blog.csdn.net/qq_39403733/article/details/87287706
版权声明:本文为博主原创文章,转载请附上博文链接! **/

如有侵权,请联系我

相关文章

  • JS_bind

    前言 bind、call、apply 三个改变指向函数,在JS面试中,经常问道。本文收录了 四种 手写bind的方...

网友评论

    本文标题:JS_bind

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