ES6 中对函数的增强,在之前的文件中多少都有看过了,例如〈增强的数值与字符串〉看过的,函数中可以使用...
运算符,用来将多于参数的实参收集在一个数组中:
> function some(a, b, ...others) {
... console.log(a);
... console.log(b);
... console.log(others);
... }
undefined
> some(10);
10
undefined
[]
undefined
> some(10, 20);
10
20
[]
undefined
> some(10, 20, 30);
10
20
[ 30 ]
undefined
> some(10, 20, 30, 40);
10
20
[ 30, 40 ]
undefined
>
这可以用来取代函数中的arguments
,在其他语言中,这个特性可能被称为不定长度实参,规则也类似,...
只能用在最后一个参数,不能有两个以上的...
。
反过来,如果一个对象是可迭代的,在它前面可以放置...
,这时称其为 Spread 运算符,与函数结合使用的时候,可以将可迭代对象的元素,逐个分配给对应的参数,例如:
> some(...[1, 2, 3, 4]);
1
2
[ 3, 4 ]
undefined
>
而在〈增强的数值与字符串〉中也看过,当函数使用标记模版时,会是一个函数的特殊调用形式,详请看参考该文件,这边不再说明了。
在〈Destructuring、Rest 与 Spread 运算〉中看过解构语法,也看过函数中,在参数设置上也可以使用解构语法,那时还玩了个函数式风格的范例:
function sum([head, ...tail]) {
return head ? head + sum(tail) : 0;
}
console.log(sum([1, 2, 3, 4, 5])); // 15
话说,透过 Rest 与 Spread,也可以写出底下有趣的函数呢!不过不鼓励这么写啦!
function sum(head, ...tail) {
return head ? head + sum(...tail) : 0;
}
console.log(sum(...[1, 2, 3, 4, 5])); // 15
在 ES6 中,每个函数实例都会有个name
特性,用来指出函数的名称,其实这个特性在 ES6 之中已经被广泛使用,只不过在 ES6 中才标准化。
> function f() {}
undefined
> f.name;
'f'
> (function() {
... }).name;
''
> let f2 = function() {};
undefined
> f2.name;
'f2'
> let f3 = f2;
undefined
> f3.name;
'f2'
>
在 ES6 之前,函数的参数无法设置默认值,若想要有默认值的效果,通常会透过侦测参数是否为undefined
来达成,在 ES6 中,函数的参数可以指定默认值了:
> function doSome(a, b, c = 10) {
... console.log(a, b, c);
... }
undefined
> doSome(1, 2);
1 2 10
undefined
> doSome(1, 2, 3);
1 2 3
undefined
>
参数的默认值,每次都会重新运算,这可以避免其他语言中有默认值,然而默认值持续被持有的问题(像是 Python):
> function f(a, b = []) {
... b.push(a);
... console.log(a, b);
... }
undefined
> f(1)
1 [ 1 ]
undefined
> f(1)
1 [ 1 ]
undefined
> f(1, [1, 2, 3])
1 [ 1, 2, 3, 1 ]
undefined
>
参数的默认值,也可以指定表达式(个人觉得应该避免使用,除非有很好的理由):
> function f(y = x + 1) {
... console.log(y);
... }
undefined
> let x = 10;
undefined
> f();
11
undefined
> x = 20;
20
> f();
21
undefined
>
> f2(1);
1 2
undefined
> f2(10);
10 11
undefined
>
理论上,你只能把有默认值的参数写在参数列的后面,不过,要玩也是可以(也是不鼓励的写法)…
> function f(a = 10, b) {
... console.log(a, b);
... }
undefined
> f(0, 2);
0 2
undefined
> f(undefined, 20);
10 20
undefined
>
函数实例有个length
特性,可以用来获取定义函数时参数的个数,不过,在使用了...
Rest 运算,或者是指定了默认值的参数,是不会计入length
的:
> function f(a, b) {}
undefined
> f.length;
2
> function f2(a, b = 10) {}
undefined
> f2.length;
1
> function f3(a, ...b) {}
undefined
> f3.length;
1
>
ES6 若是在严格模式下,支持 Tail Call Optimization,只要在调用下个函数前,当前的函数不需要保留任何状态,也就是所谓的 Proper Tail Call,ES6 规定就要进行最佳化,不需要使用一个新的 Stack frame,只需要重复使用目前的 Stack frame 就可以了。
如果你不知道什么是 Proper Tail Call,或者没听过 Tail Call Optimization,那表示你可能没遇过相对应的问题,或者很少写递归的东西,许多语言也不支持 Tail Call Optimization,就暂时记得这边曾经提过这个东西就可以了。
如果真的想知道的话,可以看一下〈Tail call〉或者参考〈递归的美丽与哀愁〉。