本文主要总结ES6新增的函数的用法及注意点。
函数参数默认值
ES6允许为函数参数设置默认值。用法如下:
function log(x, y = 'World') {
console.log(x, y);
}
log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello
复制代码
这样写的好处是:
- 比ES5写法简洁
- 便于阅读代码
- 有利于代码优化
需要注意的点:
- 参数变量是默认声明的,所以不能用let和const再次声明。但是var是可以的。
- 使用参数默认值时,函数不能有同名参数。
- 参数默认值是惰性求值的。
- 有默认值的参数最好放到后面,如果放到前面,传参无法省略;若要使用默认值,需要显示传undefined才会触发默认值
- 函数的length属性返回没有指定默认值的参数个数。而且如果设置的默认值不是尾参数,那么length属性不再计入后面的参数。
(function (a) {}).length // 1
(function (a = 5) {}).length // 0
(function (a, b, c = 5) {}).length // 2
(function (a = 0, b, c) {}).length // 0
(function (a, b = 1, c) {}).length // 1
复制代码
rest参数
rest参数的引入,用于获取函数的多余参数,这样就不需要使用arguments对象了。
function push(array, ...items) {
items.forEach(function(item) {
array.push(item);
console.log(item);
});
}
var a = [];
push(a, 1, 2, 3)
复制代码
需要注意的点:
- rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。
- 函数的length属性,不包括 rest 参数。
严格模式
ES6规定只要函数参数使用了默认值、解构赋值或者扩展运算符,那么函数内部就不能显示设定为严格模式,否则报错。 解决方案:
- 设置全局性的严格模式
'use strict';
function doSomething(a, b = a) {
// code
}
复制代码
- 把函数包裹在一个无参数的立即执行函数里
const doSomething = (function () {
'use strict';
return function(value = 42) {
return value;
};
}());
复制代码
name属性
函数的那么属性,返回函数的函数名。 需要注意的点:
- 将匿名函数赋值给一个变量,ES5的name属性返回空字符串,ES6的name属性返回实际的函数名。
var f = function () {};
// ES5
f.name // ""
// ES6
f.name // "f"
复制代码
- Function构造函数返回的函数实例,name属性的值为anonymous。
(new Function).name // "anonymous"
复制代码
- bind返回的函数,name属性值会加上bound前缀。
function foo() {};
foo.bind({}).name // "bound foo"
(function(){}).bind({}).name // "bound "
复制代码
箭头函数
用法很简单,主要记录一下需要注意的点。
- 如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。
// 报错
let getTempItem = id => { id: id, name: "Temp" };
// 不报错
let getTempItem = id => ({ id: id, name: "Temp" });
复制代码
- 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
function foo() {
setTimeout(() => {
console.log('id:', this.id);
}, 100);
}
var id = 21;
foo.call({ id: 42 });
// id: 42
复制代码
箭头函数的这个特点,使得this指向固定化。根本原因是箭头函数根本没有自己的this,导致内部的this就是外层代码的this。
- 不可以当做构造函数使用,即不可以使用new命令,否则会抛出错误。因为箭头函数实际上没有this!!!
- 不可以使用arguments对象,在箭头函数内部该对象不存在。如果要用,可以使用rest参数代替。
- 不可以使用yield命令,因此箭头函数不能用作Generator函数。
- 箭头函数内部不存在arguments、super、new.target对象
尾调用优化
尾调用指的是某个函数的最后一步是调用另一个函数。尾调用不一定出现在函数尾部,只要是最后一步操作即可。
function f(x) {
if (x > 0) {
return m(x)
}
return n(x);
}
复制代码
上面就是一个尾调用的例子。 同时,下面的几种情况不是尾调用。
// 情况一
function f(x){
let y = g(x);
return y;
}
// 情况二
function f(x){
return g(x) + 1;
}
// 情况三
function f(x){
g(x);
}
复制代码
由于函数内部调用另一个函数,会形成一个调用栈。而尾调用是函数的最后一步操作,所以尾调用的时候不需要保留外层函数的调用帧。直接用内层函数的调用帧就可以。这就是尾调用优化。
由此可知,我们把函数都写成尾调用,使得每次执行时,调用帧只有一项,这会大大节省内存。
但是需要注意一点:
- 只有不再用到外层函数的内部变量,内层函数的调用帧才会取代外层函数的调用帧,否则无法进行尾调用优化。
- 尾递归优化和它类似。
- ES6的尾调用优化只在严格模式下开启,正常模式无效。