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

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