ES6学习笔记(二)参数的默认值和rest参数

本文深入探讨了ES6中函数参数的默认值和rest参数的使用方法及注意事项,包括如何避免常见陷阱,以及这些特性如何提高代码的可读性和维护性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ES6在函数参数上有了一些很方便的改变,其一就是参数的默认值可以在写形参的时候直接写

参数默认值的使用


参数的默认值是在参数被省略或者参数为undefined时使用。

function fn(a=1){
    console.log("a: "+a);
}

fn() // "a: 1"

fn(undefined) // "a: 1"

fn(2) // "a: 2"

这两种情况看起来似乎是相同的,但在某些情况还是有些区别的,比如传入undefined时arguments的length会增加,而省略不会。

function fn(a=1,b=2){
    console.log(arguments.length);
}

fn() // 0

fn(undefined) // 1

fn(undefined,undefined) // 2

参数默认值带来的便利


在ES5中,我们如果要实现参数的默认值,一般是会在函数的第一行用到"||"来给传入的参数赋默认值

function fn(a){
    a=a||'world';
    console.log('hello '+a);
}

fn();//hello world
fn('friend');//hello friend

当我们省略参数时,a就会自动赋值为'world',当传入参数时,a就会等于输入的参数。但是这个方法有一些缺陷,在传入可以隐式转为假值的值时就会调用默认值,如下面代码

function add(a,b){
    a=a||2;
    b=b||3;
    console.log(a+b);
}
add(); // 5
add(0); // 5
add(0,0); // 5

可以看到,虽然我们传入了0,但是因为这里将0隐式转为false,根据||的用法,如果||判断中第一个值为false,那么就会将||后的值作为这个运算的最后结果,所i有a||2,也就是0||2,最后返回的是2,所以这里即便传入了0,最后a还是等于2。b也同理。

除了使用"||"外,也可以使用arguments来获取参数,从而达到同样的效果。

function fn() {
    var a = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'world';
    console.log('hello ' + a);
}

这里通过使用三目运算符判断是否有参数,若有的话将a赋值为传入的参数,没有的话则赋值为'world'。(这部分代码是我用下面要写的ES6在babel下转换生成的)

在ES6中,我们可以很方便地实现函数参数的默认值,形式几乎就是在参数位置直接赋值,很容易记住

function fn(a='world'){
    console.log('hello '+a);
}

fn();//hello world
fn('friend');//hello friend

像这样直接在参数位置赋给a默认值,十分方便。可能一个参数看起来和写一句var a=a||'world'差别不大,但是如果参数多的话,ES6提供的便利性就很明显了,而且这样也避免了一些使用||的坑。

参数默认值表达式


参数默认值不仅可以是一些简单值,也可以是一些合法的表达式,甚至是函数调用。

function fn(a=x+1,b=num()){
	console.log(a+b);
}
function num(){
    return 10;
}

var x=5;
fn(); // 16

var x=6;
fn(); // 17

上面对的代码使用的两个默认值,一个是相加的表达式,一个是函数调用。而从上面两个fn()打印出的结果不同可以看出,作为默认参数的表达式是惰性求值的,它们只在需要的时候运行。

还有一点要注意的是,参数默认值是在其自己的作用域中,即上面的代码中,a=x+1,首先是在a所在的作用域中寻找x,在当前作用域中找不到x后在外部作用域寻找x,找到后才赋值给这里的a,可以使用下面的代码更明显地看出参数默认值所在的作用域。

var z=2;
function fn(z=z){
    console.log(z);
}

fn(); // Uncaught ReferenceError: z is not defined at fn

这里在调用方法fn时,因为没有传入参数,所以要使用参数默认值,而首先在当前作用域中寻找z,而当前作用域中z是个未被初始化的参数变量,所以将z作为参数默认值会报错。

参数默认值带来的坑


ES6的默认参数虽然方便,但也有一些坑:

1.使用默认参数后不能再在函数里面使用let和const声明该变量,但是var可以

function fn(a=2){
    let a=1;
}

fn()//Uncaught SyntaxError: Identifier 'a' has already been declared
fn(1)//Uncaught SyntaxError: Identifier 'a' has already been declared

这个可以在阮一峰的ES6入门中得到解释,“参数变量是默认声明的”,所以let和const是不能再次使用的,这里的报错也可以看出'a'已经被声明了。

2.使用参数默认值时,函数不能有同名参数

function fn(a,a,b){
    console.log(a+''+b);
}

fn(1,2,3,4);//34

function fn(a,a=2,b){
    console.log(a+''+b);
}//Uncaught SyntaxError: Duplicate parameter name not allowed in this context


function fn(a,a,b=2){
    console.log(a+''+b);
}//Uncaught SyntaxError: Duplicate parameter name not allowed in this context

这里可以看到在没给参数赋默认值之前,传入相同的参数名是不会报错的,也可以传入参数执行结果,但是如果给参数赋默认值的话,不管赋默认值的是重复的参数还是没重复的参数,都会报错。

3.函数的length值会失真

指定了函数的参数默认值后,函数的length值会返回没有指定默认值的参数个数

(function fn(a,b,c){}).length//3
(function fn(a,b,c=1){}).length//2
(function fn(a,b=1,c=1){}).length//1
(function fn(a=1,b,c){}).length//0   a后面的b,c不带默认值

就上面代码来看,第一行的代码的函数参数中没有默认参数,所以length值为3,第二,三行的代码中的length值等于参数个数减去带默认值的参数的个数。而第四行我们可以看到,只有一个带默认值的参数,但是得到的length值却为0,是因为带默认参数的值后面还有不带默认值的参数,所以就会使length的值为0。

说完这部分坑,说说ES6另一个便利的地方,rest参数

在ES6之前,我们如果不确定会传入多少个参数,可以使用arguments来获取多传入的参数

function fn(a){
    if(arguments.length>1)
        for(let i=0;i<arguments.length;i++)
        	console.log(arguments[i]);
    else
        console.log(a);
}

fn(1);//1

fn(1,2,3,4);//1 2 3 4

而在ES6中,我们可以这样写

function fn(a, ...rest) {
    console.log(a);
    for (let i = 0; i < rest.length; i++)
        console.log(rest[i]);
}

fn(1);//1

fn(1,2,3,4);//1 2 3 4

这样得到的rest为一个数组对象。同样的,在使用rest参数时,也有要注意的地方,rest参数必须放在参数最后面的地方,否则会报错

function fn(a, ...rest,b) {
    console.log(a);
    for (let i = 0; i < rest.length; i++)
        console.log(rest[i]);
}//Uncaught SyntaxError: Rest parameter must be last formal parameter

参考文档:阮一峰的《ECMAScript6入门》

                  Kyle Simpson的《你不知道的JavaScript 下卷》

 


ES6学习笔记目录(持续更新中)

 

ES6学习笔记(一)let和const

ES6学习笔记(二)参数的默认值和rest参数

ES6学习笔记(三)箭头函数简化数组函数的使用

ES6学习笔记(四)解构赋值

ES6学习笔记(五)Set结构和Map结构

ES6学习笔记(六)Iterator接口

ES6学习笔记(七)数组的扩展

ES6学习笔记(八)字符串的扩展

ES6学习笔记(九)数值的扩展

ES6学习笔记(十)对象的扩展

ES6学习笔记(十一)Symbol

ES6学习笔记(十二)Proxy代理

ES6学习笔记(十三)Reflect对象

ES6学习笔记(十四)Promise对象

ES6学习笔记(十五)Generator函数

ES6学习笔记(十六)asnyc函数

ES6学习笔记(十七)类class

ES6学习笔记(十八)尾调用优化

ES6学习笔记(十九)正则的扩展

ES6学习笔记(二十)ES Module的语法

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值