zoukankan      html  css  js  c++  java
  • bind,call,apply的实现

    apply、call 和 bind 实现

    在前面的文章中介绍过了,apply 和bind 的作用就是显示改变函数执行的this的绑定。
    apply 和 call 是执行函数并该改变this,二者的参数有所区别
    而bind则是 返回一个待执行的新函数, 当新函数执行的时候改变了this的指向。
    所以,bind的情况会与apply和call不同。

    1. 我们知道在js里函数是可以被new的 因此情况会比 apply 和 call 多一种情况。
    2. bind 是可以预设参数,
      相同情况是:
      这三个函数对于没有参数 都指向的window 对于原始类型 都会包装成对象的形式

    apply的实现

    Function.prototype.myapply = function(context){
        context = context ? Object(context) : window;  // 确定对象
        var fn = Symbol(); 
        context[fn] = this;  // 绑定到当前对象上
        let result;
        let arr = [...arguments].slice(1);  // 获取参数
        // 执行函数
        if (!arr.length) {
            result = context[fn]();
        } else {
            result = context[fn](arr); 
        }
        delete context[fn];  // 删除绑定
        return result;
    }
    

    call的实现

    Function.prototype.mycall = function(context){
        context = context ? Object(context) : window;
        let fn = Symbol();
        // 绑定到当前对象上
        context[fn] = this;
        // 获取参数
        let args = [...arguments].slice(1);
        // 执行函数
        let result = context[fn](...args);
        // 删除绑定
        delete context[fn] 
        return result;
    }
    

    bind的实现

    如果参照 apply 和 call 的想法,实现方式如下:

    Function.prototype.mybind = function(context, ...perAgrs){
        context = context ? Object(context) : window;
        let fn = this; // 记录函数
        let fnBound = function(...args){
            fn.apply(context, perAgrs.concat(args) )
        }
        return fnBound;
    }
    

    此时我们正常的绑定函数是没有问题的。
    但是在 new 绑定后的函数时候就会出现问题

    function fn(a){
        this.a = a;
    }
    let t1 = {a:2};
    let t2 = {a:2};
    let mybindFn = fn.mybind(t1);
    let bindFn = fn.bind(t2);
    let objmybindFn = new mybindFn(1);
    let objbindFn = new bindFn(1);
    let objn = new fn(1);
    console.log(objmybindFn);  // fn {a: 1}
    console.log(objbindFn);   // fnBound {}
    

    我们会发现这两结果是不一样的,
    objmybindFn是 fnBound 实例化后的对象。
    objbindFn则是 fn实例化后的对象。
    其实对于objmybindFn的结果,我们也很容易理解,构造函数就是fnBound。而在fnBound的函数体内执行语句是fn.apply(context, perAgrs.concat(args) ) 所以new值设置在t2上了。
    可是我们发现原生bind的实现并不是这样的。他使用的构造函数是fn, 而且他也不会改变t1。你可以认为是直接new fn(2).
    到此,我们需要解决两个问题。

    1. 判断 new 还是 函数执行,从而确定 fn.apply(context, perAgrs.concat(args) ) 的context是谁。
    2. 原型对象的改写。
      解决方法:
    3. instanceof 可以做原型链的检查, 判断当前对象是都 new 出来的。 用于确定 context是谁。
    4. 重写 fnBound 的原型对象(方法很多)
      1. 直接让 fnBound.prototype = fn.prototype, 这样在改写 fnBound.prototype时候会影响 fn.prototype
      2. fnBound.prototype = new fn(),
      3. fnBound.prototype = Object.create(fn.prototype)
        其实是 2和3 是在 1的中间多加了一个对象, 但是原型链却相连接,这样在改写fnBound.prototype的时候只会改写创建出来的对象,但是访问的时候却可以通过原型链访问到 fn.prototype
        因此注意构建出来的对象,不能覆盖 fn.prototype 上的属性和方法。

    实现

    Function.prototype.mybind = function(context, ...perAgrs){
        context = context ? Object(context) : window;
        let fn = this; // 记录函数
        let fnBound = function(...args){
            fn.apply( this instanceof fnBound ? this : context, perAgrs.concat(args) )
        }
        fnBound.prototype = Object.create(fn.prototype);
        return fnBound;
    }
    

    依然存在问题

    function fn(a){
        this.a = a;
    }
    let mybindFn = fn.mybind({a:2});
    let bindFn = fn.bind({a:2});
    let objmybindFn = new mybindFn(1);
    let objbindFn = new bindFn(1);
    let objn = new fn(1);
    console.log(mybindFn.prototype) // {}
    console.log(bindFn.prototype) // undefined
    

    这里你就会发现,其实我们实现的结果和bind还是有所差异。

  • 相关阅读:
    Android之json解析
    关闭Android/iPhone浏览器自动识别数字为电话号码
    CSS 公共样式摘自万能的度娘
    前端必备:六款CSS工具让代码充满魅力
    移动端JS 触摸事件基础
    height:100%和height:auto的区别
    线程之生产汽车与购买汽车
    SAXCreateXMLDocument
    DOM方式创建XML文件
    java快捷键
  • 原文地址:https://www.cnblogs.com/cyrus-br/p/14031185.html
Copyright © 2011-2022 走看看