函数形参的默认值

在es5中实现默认参数功能

function makeRequest(url,timeout,callback){
    timeout = timeout || 2000;
    callback = callback || function(){ ... }
    ...
}

这里有个问题,当timeout传入的值为0时,这个值是合法的,但也会被置位2000,因为0代表false。
继续优化我们上面的方法:

function makeRequest(url,timeout,callback){
    timeout = (typeof timeout !== 'undefined') ? timeout : 2000;
    callback = (typeof callback !== 'undefined') ? callback : function(){ ... }
    ...
}

尽管这种方法已经安全的实现了配置参数默认值,但是需要额外的代码来执行这种非常基础的操作。不够简洁!

es6中的默认参数值

直接上代码:

function makeRequest(url,timeout = 2000,callback = function(){}){ ... }

只需在声明函数的形参名称后面通过"="赋予默认值即可。可以为任意参数指定默认值,在已指定默认值的参数后可以继续声明无默认值参数。
tips:null是一个合法值,因此给带有默认值的参数传递null值,该参数的值会设置为null,可简单理解为只有该参数为对应的值为nundefined时,参数的默认值才会生效

默认参数表达式

除了原始类型的值可以作为参数默认值外,非原始值也可以作为参数的默认值:

function getValue(){
    return 5;
}
function add(first,second = getValue()){
    return first + second;
}

console.log(add(1,1));  //2
console.log(add(1));    //6

tips:初次解析函数声明时不会调用getValue()方法,只有当调用add()函数且不传入第二个参数时才会调用。

正因为默认参数是在函数调用时求值,所以可以使用先定义的参数作为后定义参数的默认值(因为在声明函数时,先定义的参数已经被声明)

function add(first,second = first){
    return first + second;
}

console.log(add(1,1)); //2
console.log(add(1));   //2

tips:在引用参数默认值的时候,只允许引用前面参数的值,即先定义的参数不能访问后定义的参数:function add(first=second,second) 这样是会报错误的

clipboard.png

处理无命名参数

产生无命名参数的情况:js函数语法规定,无论函数已定义的命名参数有多少,都不限制调用时传入的实际参数数量。

es5中的无命名参数

通过arguments来操作:

function (arg1){
    for(let i=1,len=arguments.length;i<len;i++){  //第一个是命名参数,要获取未命名参数需要从arguments[1]开始
        process(arguments[i])
    }
}

这样可以实现处理多个未命名参数的要求,但是存在几个缺点:

  1. 并不容易发现这个函数可以接受任意数量的参数
  2. 因为第一个参数为命名参数,所以获取未命名参数的索引位置应该从1开始。当遇到函数有2个,3个,4个....命名参数时,再处理未命名参数就会疲于计算索引开始位置,显得不灵活

es6的不定参数

es6中,在函数的命名参数前添加三个点(...),就表明这是一个不定参数,改参数为一个数组,包含着自它之后传入的所有参数。

function (...arg){
    for(let i=0,len=arg.length;i<len;i++){
        process(arg[i]);
    }
}

不定参数的使用限制

  1. 每个函数最多只能声明一个不定参数,而且一定要放在所有参数的末尾
  2. 不定参数不能用于对象字面量setter中,会导致语法错误,例:
let obj={
    set name(...value){
        ...
    }
}

clipboard.png
之所以报错是因为setter的参数只能有一个

展开运算符

展开运算符与不定参数很相似。
不定参数可以让你指定多个各自独立的参数,并通过整合后的数组来访问;而展开运算符可以让你指定一个数组,将他们打散后作为各自独立的参数传入函数。

例如有一个需求需要你在一个数组中找出最大的那个数,es5只能用如下方式实现

let values=[25,50,75,100];
console.log(Math.max.apply(Math,values));  //因为Math.max方法不支持接收数组参数,所以只能通过apply的方式来变通解决这个问题

es6解决办法:

let values=[25,50,75,100];
console.log(Math.max(...values));  //简单明了

tips:可以将展开运算符与其它正常传入的参数混合使用:console.log(Math.max(...values,2,55,77));

箭头函数

箭头函数对比普通函数:

  • 没有this,super,arguments和new.target绑定,箭头函数中的这些值由外围最近一层非箭头函数决定
  • 不能通过new关键字调用。箭头函数没有[[construct]]方法,所以不能被用作构造函数,通过new关键字调用箭头函数,程序会抛出错误
  • 没有原型。因为不可以通过new调用箭头函数,因此没有构建原型的需求,所以箭头函数不存在prototype这个属性
  • 不可以改变this绑定
  • 不支持arguments对象,必须通过命名参数和不定参数这两种形式访问函数的参数
  • 不支持重复的命名参数

箭头函数语法

let reflect = value => value;   //只有一个参数时可以直接写参数名
let reflect = (value1,value2) => value1 + value2; //有两个参数时需要给参数加小括号
let reflect = () => 5;  //没有参数时需要一对空的小括号
let reflect = () => {
    process1();
    process2();
    ...
    return 5; //当函数体有多条语句时需要花括号包含,如果有返回值,需要显式地return ,函数体只有一条语句时,该语句的执行结果就是默认的返回值
}
let reflect = () => ({name:'zj',age:18});  //当需要return一个对象字面量时需要添加(),已指明这是返回值,而不是函数体  {}的原因
let reflect = ((name) => name;)('zj');   //立即执行函数表达式 

我和我最后的倔强
161 声望4 粉丝