• ES 6 : 函数的扩展


    1. 函数参数的默认值

    [ 基本用法 ]

      在ES6之前,不能直接为函数的参数指定默认值,只能采用变通的方法。

       上面的代码检查函数log的参数y有没有赋值,如果没有,则指定默认值为world。这种写法的缺点在于,如果参数y赋值了,但是对应的布尔值为false,则该赋值不起作用。就像以上代码的最后一行,参数y等于空字符,结果被改为默认值。

      ES6允许为函数的参数设置默认值,即直接写在参数定义的后面。

    [ 与解构赋值默认值结合使用 ]

      参数默认值可以与解构赋值的默认值结合起来使用。

    [ 参数默认值的位置 ]

      通常情况下,定义了默认值的参数应该是函数的尾参数。如果非尾部的参数设置默认值,实际上这个参数是无法省略的。

      

      有默认值的参数都不是尾参数。这时,无法只省略该参数而不省略其后的参数,除非显式输入undefined。如果传入undefined,将触发该参数等于默认值,null没有这个结果。

    [ 函数的length属性 ]

      指定了默认值之后,函数的length属性将返回没有指定默认值的参数个数。

      length属性的含义是,该函数预期传入的参数个数。某个参数指定默认值之后,预期传入的参数个数就不包括这个参数了。

    [ 作用域 ]

      如果参数默认值是一个变量,则该变量所处的作用域与其他变量的作用域规则是一样的,即先是当前函数的作用域,然后才是全局作用域。

      如果调用时函数作用域内部的变量x没有生成,结果就会不一样。

      函数调用时y的默认值变量x尚未在函数内部生成,所以x指向全局变量。

    [ 应用 ]

      利用参数默认值,可以指定某一个参数不得省略,如果省略就抛出一个错误。

      以上代码中的foo函数,如果调用的时候没有参数,就会调用默认值throwIfMissing函数,从而抛出一个错误。

     2. rest参数

      ES6引入了rest参数(形式为"...变量名"),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest参数搭配的变量是一个数组,该变量将多余的参数放入其中。

      以上代码中的add函数是一个求和函数,利用rest参数可以向该函数传入任意数目的参数。下面是rest参数代替arguments变量的例子。

      // arguments 变量的写法

      // rest 变量的写法

      注意:rest参数之后不能有其他参数,函数的length属性不包括rest参数。

    3. 扩展运算符

    [ 含义

      扩展运算符是三个点(...)。它好比rest参数的逆运算,将一个数组转为用逗号分隔的参数序列。

      扩展运算符与正常的函数参数可以结合使用。

    function f(v,w,x,y,z){}
    var args = [0,1];
    f(-1,...args,2,...[3]);

    [ 替代数组的apply方法 ]

      由于扩展运算符可以展开数组,所以不再需要apply方法将数组转为函数的参数了。下面是扩展运算符取代apply方法的一个实际的例子,应用Math.max方法简化求数组最大元素的写法:

    // ES5的写法
    Math.max.apply(null,[14,3,77])
    // ES6的写法
    Math.max(...[14,3,77])

    [ 扩展运算符的应用 ]

    - 合并数组

      扩展运算符提供了数组合并的新写法。

    - 与解构赋值结合

      扩展运算符可以与解构赋值结合起来用于生成数组。

      如果将扩展运算符用于数组赋值,只能放在参数的最后一位,不然会报错。

    - 函数的返回值

      JavaScript的函数只能返回一个值,如果需要返回多个值,只能返回数组或对象。扩展运算符提供了解决这个问题的一种变通方法。

    var dateFields = readDateFields(database);
    var d = new Date(...dateFields);

      上面的代码用于从数据库中取出一行数据,通过扩展运算符直接将其传入构造函数Date。

    - 字符串

      扩展运算符还可以将字符串转为真正的数组。

    [ 类似数组的对象 ]

      任何类似数组的对象都可以用扩展运算符转为真正的数组。

    var nodeList = document.querySelectorAll("div");
    var array = [...nodeList];

      上面的代码中,querySelectorAll方法返回的是一个nodeList对象,扩展运算符可以将其转为真正的数组。

    [ Map 和 Set 结构,Generator 函数 ]

      扩展运算符内部调用的是数据结构的Iterator接口,因此只要具有Iterator接口的对象,都可以使用扩展运算符,比如Map结构。

    [ name属性

      函数的name属性返回该函数的函数名。

      如果将一个匿名函数赋值给一个变量,ES5的name属性会返回空字符串,而ES6的name属性会返回实际的函数名。

      如果将一个具名函数赋值给一个变量,则ES5和ES6的name属性都返回这个具名函数原本的名字。

    5. 箭头函数

    [ 基本用法 ]

      ES6允许使用"箭头"(=>)定义函数。

    var f = v => v;
    
    // 上面的箭头函数等同于
    var a = function(v){
        return v;
    };

      如果箭头函数不需要参数或需要多个参数,就使用圆括号代表参数部分。

    var f = () => 5;
    // 等同于
    var f = function(){ return 5 };
    
    var sum = (num1,num2) => num1 + num2;
    // 等同于
    var sum = function(num1,num2){
        return num1+num2;
    };

      如果箭头函数的代码块部分多于一条语句,就要使用大括号将其括起来,并使用return语句返回。

    var sum = (num1,num2) => { return num1 + num2 }

      由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号。

    var getTempItem = id => ({ id: id, name: "Temp" });

      箭头函数可以与变量解构结合使用。

    const full = ({ first, last }) => first + '' + last;
    
    // 等同于
    function full(person){
        return person.first + '' + person.name;
    }

      箭头函数使得表达更加简洁。

    const isEven = n => n % 2 == 0;

      箭头函数的一个用处是简化回调函数。

    // 正常函数的写法
    [1,2,3].map(function(x){
        return x*x;
    });
    
    // 箭头函数的写法
    [1,2,3].map(x => x*x);

      箭头函数还能与rest参数结合。

    [ 使用注意点 ]

      箭头函数有几个使用注意点。

      ● 函数体内的this对象就是定义时所在的对象,而不是使用时所在的对象。

      ● 不可以当做构造函数。也就是说,不可以使用new命令,否则会抛出一个错误。

      ● 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用rest参数代替。

      ● 不可以使用yield命令,因此箭头函数不能用作Generator函数。

     6.函数绑定

      箭头函数没有自己的this,导致内部的this就是外层代码块的this。正因为它没有this,所以也就不能用作构造函数。当然也不能使用call()、apply()、bind()这些方法去改变this的指向。

      函数绑定运算符是并排的双冒号(::),双冒号左边是一个对象,右边是一个函数。该运算符会自动将左边的对象作为上下文环境绑定到右边的函数上。

    foo::bar
    // 等同于
    bar.bind(foo)
    
    foo::bar(...arguments)
    // 等同于
    bar.bind(foo,arguments)

      如果双冒号左边为空,右边是一个对象的方法,则等于将该方法绑定在该对象上。

    var method = obj::obj.foo
    // 等同于
    var method = ::obj.foo

      由于双冒号运算符返回的还是原对象,因此可以采用链式写法。

    import {map,takeWhild,forEach} from "iterlib";
    getPlayers()
    ::map(x => x.character())
    ::takeWhile(x => x.strength > 100);
    ::forEach(x => console.log(x));

    7.尾调用优化

    [ 尾调用概念 ]

      尾调用是函数式编程的一个重要概念,本身非常简单,一句话就能说清楚,就是指某个函数的最后一步是调用另一个函数。

    function f(x){
        return g(x)
    }

      以下情况都不属于尾调用:

    // 情况一
    function f(x){
        let y = g(x);
        return y;
    }
    
    // 情况二
    function f(x){
        return g(x)+1;
    }
    
    // 情况三
    function f(x){
        g(X);
    }

      上面的代码中,情况一是调用函数g之后还有赋值操作,所以不属于尾调用,即使语义完全一样;情况二也属于调用后还有操作,即使写在一行内;情况三等同于下面的代码。

    function f(x){
        f(x);
        return undefined;
    }

     

  • 相关阅读:
    python基础之数值类型
    第一个python学习笔记
    入学潭州科技教育学习python语言
    Python 编码规范(Google)
    Python
    博客开通
    文件、异常、文件的输入输出
    内置函数,变量的作用域,闭包及递归
    序列类型各自的方法(1)
    函数基础
  • 原文地址:https://www.cnblogs.com/koto/p/5940352.html
Copyright © 2020-2023  润新知