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

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的语法

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值