1、函数参数的默认值
es6 之前不能直接为函数的参数指定默认值,只能采用变通方法
function fun (x, y) { y = y || 'world' console.log( x ,y)}fun ('hello') // hello worldfun ('hello', 'china') // hello chinafun ('hello', '') // hello world
以上代码的缺陷在于,当参数 y 所赋值的类型为 false时,该赋值不起作用,就像 y 赋值为空字符,结果被改成了 'world'
为了避免这个问题,通常需要先判断一下参数y是否被赋值,如果没有,再等于默认值
if (typeof y === 'undefined') { y = 'World';}
es6 允许为函数的参数设置默认值,直接写在参数定义的后面
function test(x, y = 'world'){ console.log('默认值',x,y); } test('hello'); // 默认值 hello world test('hello','kill'); // 默认值 hello kill
这里需要注意以下 4点
(1)、参数变量是默认声明的,所以不能用let或const再次声明
function foo(x = 5) { let x = 1; }foo () // Uncaught SyntaxError: Identifier 'x' has already been declared
(2)、使用参数默认值时,函数不能有同名参数
// 不报错function foo(x, x, y) { // ...}// 报错function foo(x, x, y = 1) { // ...}// SyntaxError: Duplicate parameter name not allowed in this context
(3)、参数默认值不是传值的,而是每次都重新计算默认值表达式的值。也就是说,参数默认值是惰性求值的
let x='test';function test2(x,y=x){ console.log(x,y); // x = test or x = kill} test2('kill'); // kill kill
(4)、如果传入undefined,将触发该参数等于默认值,null则没有这个效果
function foo(x = 5, y = 6) { console.log(x, y);}foo(undefined, null) // 5 null
2、rest 参数
rest 参数(形式为 ...arg),用于获取函数多余的参数,存放到数组中
function test3(...arg){ for(let v of arg){ console.log('rest',v); } } test3(1,2,3,4,'a');// rest 1// rest 2// rest 3// rest 4// rest a
再来看一个给类数组对象排序的例子
// es5function sortNumbers() { return Array.prototype.slice.call(arguments).sort();}// es6const sortNumbers = (...numbers) => numbers.sort();
rest 参数中的变量代表一个数组,所以数组特有的方法都可以用于这个变量。下面是一个利用 rest 参数改写数组push方法的例子
function push(array, ...items) { items.forEach(function(item) { array.push(item); console.log(item); });}var a = [];push(a, 1, 2, 3) // 1 2 3
注意,rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错
// 报错function f(a, ...b, c) { // ...}
函数的length属性,不包括 rest 参数
(function(a) {}).length // 1(function(...a) {}).length // 0(function(a, ...b) {}).length // 1
3、箭头函数
es6 允许使用“箭头”(=>)定义函数
let arrow = v => v*2// 等同于function arrow (v) { return v*2}let arrow2 = () => 5// 等同于function arrow2 () { return 5}
如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用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.last;}
箭头函数有几个使用注意点。
(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象
(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误
(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替
(4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数
上面四点中,第一点尤其值得注意。this对象的指向是可变的,但是在箭头函数中,它是固定的
function foo() { setTimeout(() => { console.log('id:', this.id); }, 100);}var id = 21;foo.call({ id: 42 }); // id: 42
箭头函数可以让setTimeout里面的this,绑定定义时所在的作用域,而不是指向运行时所在的作用域。下面是另一个例子
function Timer() { this.s1 = 0; this.s2 = 0; // 箭头函数 setInterval(() => this.s1++, 1000); // 普通函数 setInterval(function () { this.s2++; }, 1000);}var timer = new Timer();setTimeout(() => console.log('s1: ', timer.s1), 3100); // s1: 3setTimeout(() => console.log('s2: ', timer.s2), 3100); // s2: 0
上面代码中,Timer函数内部设置了两个定时器,分别使用了箭头函数和普通函数。前者的this绑定定义时所在的作用域(即Timer函数),后者的this指向运行时所在的作用域(即全局对象)。所以,3100毫秒之后,timer.s1被更新了3次,而timer.s2一次都没更新