• 编写高质量JS代码的68个有效方法(五)


    No.21、使用apply方法通过不同数量的参数调用函数

    Tips:

    1. 使用apply方法自定一个可计算的参数数组来调用可变参数的函数
    2. 使用apply方法的第一个参数给可变参数的方法提供一个接收者

    //示例:计算给定数据的最大值
    function getMaxNum(){
        var max = arguments[0];
        for(var i = 1, len = arguments.length;i < len; i++){
            if(max < arguments[i]){
                max = arguments[i];
            }
        }
        return max;
    }
    getMaxNum.apply(null,[1,3,4]);
    

    该方法和call()方法功能基本类似,差别在于参数写法不一样。

    No.22、使用arguments创建可变参数的函数

    Tips:

    1. 使用隐式的arguments对象实现可变参数的函数
    2. 考虑对可变参数的函数提供一个额外的固定元数的版本,从而使用者无需借助apply方法。

    每一个函数内部都有一个arguments对象包含所有传递的参数

    function fun1(){
        console.log(arguments);
    }
    fun1('1');
    fun1(1,'2','str');
    

    No.23、永远不要修改arguments的值

    Tips:

    1. 永远不要修改arguments的值
    2. 使用[].slice.call(arguments)将arguments对象赋值到一个真正的数组中再进行修改

    arguments看起来像是数组,但是它并不是标准的数组,所以不支持数组的原型方法

    function fun1(nums){
        var lastParam = arguments.pop(); //报错,undefined is not a function。
        console.log(arguments);
    }
    
    fun1([1, 2, 3]);
    

    正确的做法是,将arguments转换为真正的数组,再进行操作,代码如下:

    function fun1(nums){
        var argArr = [].slice.call(arguments);
        var lastParam = argArr.pop();
        console.log(arguments);
    }
    
    fun1([1, 2, 3]);
    

    注意:永远不要修改arguments对象是更为安全的。

    No.24、使用变量保存arguments的引用

    Tips:

    1. 当引用arguments时当心函数嵌套层级
    2. 绑定一个明确作用域的引用到arguments变量,从而可以再嵌套的函数中引用它

    首先,先来看一段代码的输出:

    function fun1(){
        var i = 0;
        console.log(arguments);
        return {
            next:function(){
                return arguments[i++]; 
            }
        }
    }
    var f = fun1(1,2,3,4);
    console.log(f.next()); //猜猜是啥?
    

    arguments是函数中的隐式变量,每个函数都会有这样的一个隐式对象。所以最后一个console的结果可想而知。所以遇到这种场景,是建议用变量保存arguments的引用,也能让嵌套函数正确的进行对象引用,正确代码如下:

    function fun1(){
        var i = 0;
        var args = arguments;
        return {
            next:function(){
                return args[i++]; 
            }
        }
    }
    var f = fun1(1,2,3,4);
    console.log(f.next());
    

    No.25、使用bind方法提取具有确定接收者的方法

    Tips:

    1. 要注意,提取一个方法不会将方法的接收者绑定到该方法的对象上
    2. 当给高阶函数传递对象方法时,使用匿名函数在适当的接收者上调用该方法
    3. 使用bind方法创建绑定到适当接收者的函数

    老规矩,看代码:(代码1)

    var buffer = {
        entries: [],
        add: function(value){
            this.entries.push(value);
        },
        concat: function(){
            return this.entries.join('');
        }
    };
    

    该代码在直接使用时是没有问题的,思考下,由于高阶函数将函数/方法作为变量传递,那么可以有如下用法:(代码2)

    var arr = ['Jay', '.M', '.Hu'];
    arr.forEach(buffer.add);
    console.log(buffer.concat()); //思考下这个结果是什么?
    

    以上代码在arr.forEach处已经报错,Cannot read property 'push' of undefined。因为这个时候的涉及到this的指向问题。我们可以改造下buffer代码,输出this让我们看看:(代码3)

    var buffer = {
        entries: [],
        add: function(value){
            console.log(this);
            this.entries.push(value);
        },
        concat: function(){
            return this.entries.join('');
        }
    };
    

    从输出结果我们可以看到这个this,在(代码2)的执行环境中,指向的是window对象,所以导致了报错,那么如何避免这样的问题呢?针对forEach,我们有三个方法:(代码4)

    //方式一,去掉this,直接用buffer对象引用
    var buffer = {
        entries: [],
        add: function(value){
            buffer.entries.push(value);
        },
        concat: function(){
            return buffer.entries.join('');
        }
    };
    
    //方式二,指定接收者,forEach方法提供,其他方法不一定提供
    var arr = ['Jay', '.M', '.Hu'];
    arr.forEach(buffer.add, buffer);
    console.log(buffer.concat());
    
    //方式三,通过用函数包装调用,来实现指定接收者
    var arr = ['Jay', '.M', '.Hu'];
    arr.forEach(function(s){
        buffer.add(s);
    });
    console.log(buffer.concat());
    

    针对这样的问题,ES5标准库中提供了一个bind()函数来实现这样的方法。只需要如下代码:

    var arr = ['Jay', '.M', '.Hu'];
    arr.forEach(buffer.add.bind(buffer));
    console.log(buffer.concat());
    

    该bind()函数,利用buffer.add.bind(buffer)创建了一个新函数而不是修改了buffer.add函数。新函数行为就像原来函数的行为,但它的接收者被重新指定了。所以调用bind方法是安全的,即使是一个可能在程序的其他部分被共享的函数。

  • 相关阅读:
    《RabbitMQ 实战》读书笔记
    使用jstack命令查看CPU高占用的问题记录
    两种常见的单元测试方式(笔记)
    Apache Solr入门教程(转)
    搜索引擎选择: Elasticsearch与Solr(转)
    CopyOnWriteArrayList与Collections.synchronizedList的性能对比(转)
    理解list和vector的区别
    从上往下打印出二叉树的每个节点,同层节点从左至右打印。
    TypeError: Object function (req, res, next) { app.handle(req, res, next); } has no method 'configure'
    Cannot find module 'crc'
  • 原文地址:https://www.cnblogs.com/humin/p/4147783.html
Copyright © 2020-2023  润新知