函数使用扩展
一、函数默认参数
1、默认参数用法
ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。
function say(x, y = 'Guangzhou') {
console.log(x, y);
}
say('Hello') // Hello Guangzhou
say('Hello', 'Shenzhen') // Hello Shenzhen
say('Hello', '') // Hello
2、参数作用域
函数声明初始化时,如果设置了参数默认值,则参数会形成一个单独的作用域。等到初始化结束,这个作用域就会消失。
var x = 1;
function say(x, y = x) {
console.log(y);
}
say(2) // 2
上面代码中,参数 y 的默认值等于变量 x。调用函数 say 时,参数形成一个单独的作用域,在这个作用域中,默认值变量 x 指向第一个参数 x,而非全局变量 x,所以输出是 2。
再看下面的例子。
let x = 1;
function say(y = x) {
let x = 2;
console.log(y);
}
say() // 1
上面代码中,函数 say 调用时,参数 y = x 形成一个单独的作用域,这个作用域中,变量 x 本身没有定义,所以指向外层的全局变量 x。函数调用时,函数体内部的局部变量 x 影响不到默认值变量 x。
如果此时,全局变量 x 不存在,就会报错:
function say(y = x) {
let x = 2;
console.log(y);
}
say() // ReferenceError: x is not defined
二、rest 参数
ES6 引入 rest
参数(形式为...变量名),用于获取函数的多余参数,这样就不需要使用 arguments
对象了。
function test(...arg) {
for (let x of arg) {
console.log(x);
}
}
test(1, 2, 3);
// 输出:
// 1
// 2
// 3
下面是一个求和函数,利用 rest
参数,向 add 函数传入任意数目的参数:
function add(...values) {
let sum = 0;
for (var x of values) {
sum += x;
}
return sum;
}
add(1, 2, 3) // 6
下面用 rest
参数代替 arguments
变量:
// arguments 变量的写法
function sortNumbers() {
return Array.prototype.slice.call(arguments).sort();
}
// rest 参数的写法
const sortNumbers = (...numbers) => numbers.sort();
arguments
对象不是数组,而是一个类数组对象。所以为了使用数组的方法,必须使用 Array.prototype.slice.call
先将其转为数组。
rest
参数就不存在这个问题,它就是一个真正的数组,数组特有的方法都可以使用。
三、箭头函数
1、箭头函数用法
ES6 允许使用“箭头”(=>)定义函数。
var f = v => v;
// 等同于
var f = function (v) { return v; };
若箭头函数不需要参数,则用一个 () 代表参数部分。
var f = () => 5;
// 等同于
var f = function () { return 5 };
由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。
// 报错
let getTemp = id => { id: id, name: "Temp" };
// 不报错
let getTemp = id => ({ id: id, name: "Temp" });
如果箭头函数只有一行语句,且不需要返回值,则可以省略 { }。
let fn = () => void doesNotReturn();
下面是 rest
参数与箭头函数结合的例子。
const numbers = (...nums) => nums;
numbers(1, 2, 3, 4, 5) // [1, 2, 3, 4, 5]
2、几个注意点
- 箭头函数的
this
对象是定义时所在的对象,而非使用时所在的对象;(「点击查看 this 情况汇总」) - 箭头函数不可以当作构造函数,也就是说,不可以使用
new
命令,否则会报错; - 箭头函数不可以使用
arguments
对象,该对象在函数体内不存在,如果要用,可以用rest
参数代替; - 由于箭头函数没有自己的
this
,所以当然也就不能用call()
、apply()
、bind()
这些方法去改变this
指向。
3、嵌套的箭头函数
箭头函数内部,还可以再使用箭头函数。下面是一个 ES5 语法的多重嵌套函数。
function insert(value) {
return {
into: function (array) {
return {
after: function (afterValue) {
array.splice(array.indexOf(afterValue) + 1, 0, value);
return array;
}
};
}
};
}
insert(2).into([1, 3]).after(1); //[1, 2, 3]
上面这个函数,可以使用箭头函数改写:
let insert = (value) => ({
into: (array) => ({
after: (afterValue) => {
array.splice(array.indexOf(afterValue) + 1, 0, value);
return array;
}
})
});
insert(2).into([1, 3]).after(1); //[1, 2, 3]
四、尾调用
尾调用指某个函数的最后一步是调用另一个函数。
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);
}