ES 6 : 函数的扩展

时间:2022-05-25 00:35:17

1. 函数参数的默认值

[ 基本用法 ]

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

ES 6 : 函数的扩展

ES 6 : 函数的扩展

ES 6 : 函数的扩展

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

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

ES 6 : 函数的扩展

ES 6 : 函数的扩展

ES 6 : 函数的扩展

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

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

ES 6 : 函数的扩展

ES 6 : 函数的扩展

ES 6 : 函数的扩展

ES 6 : 函数的扩展

[ 参数默认值的位置 ]

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

  ES 6 : 函数的扩展

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

[ 函数的length属性 ]

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

ES 6 : 函数的扩展

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

[ 作用域 ]

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

ES 6 : 函数的扩展

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

ES 6 : 函数的扩展

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

[ 应用 ]

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

ES 6 : 函数的扩展

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

 2. rest参数

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

ES 6 : 函数的扩展

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

  // arguments 变量的写法

ES 6 : 函数的扩展

  // rest 变量的写法

ES 6 : 函数的扩展

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

3. 扩展运算符

[ 含义 ]

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

ES 6 : 函数的扩展

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

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])

[ 扩展运算符的应用 ]

- 合并数组

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

ES 6 : 函数的扩展

- 与解构赋值结合

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

ES 6 : 函数的扩展

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

ES 6 : 函数的扩展

- 函数的返回值

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

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

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

- 字符串

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

ES 6 : 函数的扩展

[ 类似数组的对象 ]

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

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

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

[ Map 和 Set 结构,Generator 函数 ]

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

ES 6 : 函数的扩展

[ name属性 ]

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

ES 6 : 函数的扩展

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

ES 6 : 函数的扩展

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

ES 6 : 函数的扩展

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参数结合。

ES 6 : 函数的扩展

[ 使用注意点 ]

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

  ● 函数体内的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;
}