一、 函数参数的默认值
1、基本用法
ES6
允许为函数的参数设置默认值,直接写在参数定义的后面。
function log (t = "hello",value = "world") {
console.log(t,value)
}
log(); // => "hello world"
log('h'); // => "h world"
log("h","w"); // => "h w"
- 参数变量是默认声明的,不能用
let
或者const
再次声明
function foo(x = 5) {
let x = 1; // error
const x = 2; // error
}
- 使用参数默认值的时候,函数不能有同名参数
function a (x,x,y =1){
// do something
}
// 报错
- 参数默认值不是传值的,而是每次都重新计算默认值表达式的值(惰性求值)
let x = 99;
function a (y = x +1) {
console.log(y);
}
a(); // => 100;
x = 100;
a(); // => 101
2、与结构赋值默认值结合使用
function a({x,y=5} = {}){
console.log(x,y);
}
a(); // => undefined 5
如下:两个函数都对参数设定了默认值,区别在
- 写法一 函数参数的默认值是空对象,但是设置了对象解构赋值的默认值
- 写法二 函数参数的默认值是一个有具体属性的对象,没设置解构赋值默认值
// 写法一
function m1({x = 0, y = 0} = {}) {
return [x, y];
}
// 写法二
function m2({x, y} = { x: 0, y: 0 }) {
return [x, y];
}
// 函数没有参数的情况
m1() // [0, 0]
m2() // [0, 0]
// x 和 y 都有值的情况
m1({x: 3, y: 8}) // [3, 8]
m2({x: 3, y: 8}) // [3, 8]
// x 有值,y 无值的情况
m1({x: 3}) // [3, 0]
m2({x: 3}) // [3, undefined]
// x 和 y 都无值的情况
m1({}) // [0, 0];
m2({}) // [undefined, undefined]
m1({z: 3}) // [0, 0]
m2({z: 3}) // [undefined, undefined]
2、参数默认值的位置
- 通常情况下,定义了默认值的参数,应该是函数的尾参数,因为这样才比较容易看出来那些参数被省略。
3、函数的length属性
- 指定了参数默认值后,
length
属性将失真
(function (a) {}).length // 1
(function (a = 5) {}).length // 0
(function (a, b, c = 5) {}).length // 2
4、作用域
- 一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域,等到初始化结束这个作用域就会消失,这种语法行为,在不设置参数默认值时,是不会出现的。
var x = 1;
function f(x, y = x) {
console.log(y);
}
f(2); // => 2
上面代码中,参数y
的默认值等于变量x
,调用函数f
时,参数形成一个单独的作用域,在这个作用域里,默认值变量x
指向第一个参数x
,而不是全局变量x
,所以输出2
。
5、应用
- 利用参数默认值,可以指定某一参数不得省略,省略就抛出错误
function throwIfMissing() {
throw new Error('Missing parameter');
}
function foo(mustBeProvided = throwIfMissing()) {
return mustBeProvided;
}
foo()
// Error: Missing parameter
二、rest参数
ES6
引入rest参数(形式为...变量名
),用来获取函数的多余参数,这样就不需要使用arguments
对象了
- rest参数搭配的变量是一个数组,该变量将多余的参数放入数组中
function add(...values){
console.log(values); // => [2,3,5]
}
add(2,3,5);
- rest参数之后不能再有其他参数,否则会报错
// 报错
function f(a, ...b, c) {
// ...
}
- 函数的
length
属性,不包括rest参数
(function(...a) {}).length // 0
(function(a, ...b) {}).length // 1
三、箭头函数
var f = v => v;
// 等同于
var f = function (v) {
return v;
};
- 如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。
var f = () => 5;
var sum = (num1, num2) => num1 + num2;
- 如果箭头函数的代码块部分多余一条语句,就要使用大括号将它们括起来,并使用
return
语句返回。
var sum = (num1, num2) => { return num1 + num2; }
- 由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。
// 报错
let getTempItem = id => { id: id, name: "Temp" };
// 不报错
let getTempItem = id => ({ id: id, name: "Temp" });
- 箭头函数可以与变量结构结合使用
const full = ({ first, last }) => first + ' ' + last;
// 等同于
function full(person) {
return person.first + ' ' + person.last;
}
- 箭头函数的一个用处是简化回调函数。
// 正常函数写法
[1,2,3].map(function (x) {
return x * x;
});
// 箭头函数写法
[1,2,3].map(x => x * x);
箭头函数使用注意点:
1、函数体内的this
对象,就是定义时所在的对象,而不是使用时所在对象
2、不可当构造函数,也就是说,不可以使用new
命令
3、不可以使用arguments
对象,该对象在函数体内部存在,用rest参数代替
4、不可使用yield
命令,因此箭头函数不能做Generator
函数
四、尾调用
尾调用是函数式编程的一个重要概念,指某个函数的最后一步是调用另一个函数
function f(x){
return g(x);
}
五、柯里化
函数式编程有一个概念,叫做柯里化,意思是将多参数的函数转换成单参数的形式
function currying(fn,n){
return function(m){
return fn.call(this,m,n); // 调用传入函数本身,并传入值 n 相当于默认值
}
}
function tail(m,n){
console.log("n",n);
console.log("m",m);
}
var f = currying(tail,1); // 传入函数,指定默认值
f(5);
// => "n" 1
// => "m" 5
- 其实可以采取
ES6
的函数默认值
function tail(m,n = 1){
console.log("n",n);
console.log("m",m);
}
tail(5);
// => "n" 1
// => "m" 5