• js重写内置的call、apply、bind


    首先看call和apply,第一个参数就是改变的this指向,写谁就是谁,如果是非严格模式下,传递null或undefined指向的也是window,二者唯一的区别是执行函数时,传递的参数方式不同,call是一个个的传递,apply是把需要传递的参数放到数组中整体传递。
    ·func.call([context], x, y)
    ·func.apply([context], [x, y])
    再看bind,它和call和apply都是改变this并且传递一些参数,不同于call和apply在改变this的同时直接把函数就执行了,bind不会立即执行函数。
    let obj = {
        fn(x, y) {
            console.log(this, x, y);
        }
    };
            
    obj.fn.call({}, 10, 20); // {}, 10, 20
    obj.fn.apply(window, [10, 20]); //window, 10, 20
    
    setTimeout(obj.fn.bind(30, 10, 20), 1000); //Number(30), 10, 20
     

    先试着重写一下bind:

    从参数看,首先是传递一个this指向并需要做一下处理,后续还有若干个参数

    function bind(context) {
        //context可能是null或undefined,需要处理一下
        if (context == undefined) {
            context = window;
        }
        //借用数组的slice方法结合arguments获取传递的指定this之后的参数集合
        var args = [].slice.call(arguments, 1);
    }

    bind函数中的this是指最终要执行的函数,而且执行bind的时候会返回一个新的匿名函数,并且在这个新的函数中执行最终要执行的函数也就是this,并且改变其this指向:

    function bind(context) {
        //context可能是null或undefined,需要处理一下
        if (context == undefined) {
            context = window;
        }
        //借用数组的slice方法结合arguments获取传递的指定this之后的参数集合
        var args = [].slice.call(arguments, 1);
        //需要最终执行的函数
        var _this = this;
        return function anonymous() {
            _this.apply(context, args);
        };
    }    

    这个bind函数大体算是写完,但还是有些问题,比如给元素进行事件绑定,div.onclick = obj.fn.bind(window, 10, 20),元素进行点击的时候,会有ev事件对象,相当于在执行bind函数返回的那个匿名函数中也需要传递参数,而且参数个数不确定,当然,最后还需要改写一下原型上的方法:

    function bind(context) {
        //context可能是null或undefined,需要处理一下
        if (context == undefined) {
            context = window;
        }
        //借用数组的slice方法结合arguments获取传递的指定this之后的参数集合
        var args = [].slice.call(arguments, 1);
        //需要最终执行的函数
        var _this = this;
        return function anonymous() {
            var amArg = [].slice.call(arguments, 0);
            _this.apply(context, args.concat(amArg));
        };
    } 
    Function.prototype.bind = bind;

    这样,这个bind函数的重写算是完成了。

    我们用es6改写一下:

    function bind(context = window, ...args) {
        return (...amArg) => this.call(context, ...args.concat(amArg));
    }

    代码看上去确实精简不少,当然也可以用apply,但经测试,性能不如call。

    接下来看看重写call:

     它会有若干个参数,第一个是要指向的this,直接用es6写法:

    function call(context = window, ...args) {
    
    }

    函数中的this就是要调用call方法的函数,想让该函数执行并且其内部this指向传递进来的context,那么形如context.函数可以做到:

    function call(context = window, ...args) {
        //给context增加一个$fn属性,把当前函数赋给这个属性      
        context.$fn = this;
        //让context.$fn这个方法执行,就是之前this函数执行,并且this指向的是context
        let result = context.$fn(...args);
        //增加完方法应该删除 
        delete context.$fn;
        return result;
    }
    Function.prototype.call = call;

    电脑刺绣绣花厂 http://www.szhdn.com 广州品牌设计公司https://www.houdianzi.com

    apply也就出来了:

    function apply(context = window, args) {
        context.$fn = this;
        let result = context.$fn(...args);
        delete context.$fn;
        return result;
    }
    Function.prototype.apply = apply;

    这里边还是有两个问题,一是$fn属性没有删除,目前还没想到解决办法,一个就是传进来的context必须是引用类型,但其实可以是基础类型:

    function call(context = window, ...args) {
        context === null ? context = window : null;
        let type = typeof context;
        if (type !== "object" && type !== "function" && type !== "symbol") {
            //=>基本类型值
            switch (type) {
                case 'number':
                    context = new Number(context);
                    break;
                case 'string':
                    context = new String(context);
                    break;
                case 'boolean':
                    context = new Boolean(context);
                    break;
            }
        }
        context.$fn = this;
        let result = context.$fn(...args);
        delete context.$fn;
        return result;
    }

    apply的判断就不写了,但基本都实现了重写,当然,这几个方法毕竟是js的内置写法,我们只是想大致实现它们的实现原理。

  • 相关阅读:
    bzoj3064: Tyvj 1518 CPU监控
    bzoj3272: Zgg吃东西&&3267: KC采花
    bzoj2759: 一个动态树好题
    bzoj4594: [Shoi2015]零件组装机
    bzoj4873: [Shoi2017]寿司餐厅
    bzoj4593: [Shoi2015]聚变反应炉
    codeforces 739E
    bzoj2034: [2009国家集训队]最大收益
    mybatis-generator使用心得
    Linux 各种软件的安装-Jenkins和svn结合
  • 原文地址:https://www.cnblogs.com/xiaonian8/p/13859160.html
Copyright © 2020-2023  润新知