2019-02-18

函数的扩展

1、函数参数的默认值

基本用法

ES6之前,不能直接为函数的参数指定默认值,只能采用变通的方法

function log(x,y){
    y=y || 'World';
    console.log(x,y);
}

log('Hello')//Hello World 
log('Hello','China')//Hello China
log('Hello','')//Hello World

上面的代码检查函数log的参数y有没有赋值,如果没有则指定默认值World。
缺点在于,如果参数y赋值了,但是对应的布尔值为false,则该赋值不起作用

为了避免这个问题,通常先判断一下参数y是否被赋值,如果没有,再等于默认值

if (typepf y === 'undefined'){
    y='World';
}

//ES6允许为函数的参数设置默认值,即直接写在参数定义的后面
function log(x,y='World'){
    console.log(x,y);
}

log('Hello')  //Hello World
log('Hello','China')  //Hello China
log('Hello','')  //Hello 

ES6的写法除了简洁,还有两个好处:
◆ 首先,阅读代码的人,可以立刻意识到哪些参数是可以省略的,不用查看函数体或文档
◆ 其次,有利于将来的代码优化,即使未来的版本在对外接口中,彻底拿掉这个参数,也不会导致以前的代码无法运行。

参数变量是默认声明的,所以不能用let或const再次声明,否则会报错

function foo(x=5){
    let x=1; //error
    const x=2; //error
}  

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

//不报错
function foo(x,x,y){
    //...
}

//报错
function foo(x,x,y=1){
    //...
}

另外一个容易忽略的地方是,参数默认值不是传值的,而是每次都重新计算默认值表达式的值。也就是说,参数默认值是惰性求值的。

let x=99;
function foo(p=x+1){
    console.log(p);
}
foo() //100

x=100;
foo() //101 参数p的默认值是x+1.这时每次调用函数foo都会重新计算x+1,而不是默认p等于100

与解构赋值默认值结合使用

参考默认值可以与解构赋值的默认值,结合起来使用

function foo({x,y = 5}){
    console.log(x,y);
}

foo({}) //undefined 5
foo({x:1}) //1 5
foo({x:1,y:2}) //1 2
foo() //TypeError: Cannot read property 'x' of undefined

上面代码只使用了对象的解构赋值默认值,没有使用函数参数的默认值。
只有当函数foo的参数是一个对象时,变量x和y才会通过解构赋值生成。
如果函数foo调用时没有提供参数,变量x和y就不会生成
通过提供函数参数的默认值就可以避免这种情况

function foo({x,y = 5}={}){
    console.log(x,y);
}
foo() //undefined 5
//上面代码指定,如果没有提供参数,函数foo的参数默认为一个空对象
function fetch(url,{body = ' ',method = 'GET',headers = {} }){
      console.log(method);
}

fetch('http://example.com',{})
//GET
fetch('http://example.com')
//TypeError: Cannot destructure property `body` of 'undefined' or 'null'.

练习题,下面两种写法有什么差别?

//写法一
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]

参考默认值的位置

通常情况下,定义了默认值的参数,应该是函数的尾参数。因为这样比较容易看出来到底省略了哪些参数。

//例一
function f (x=1,y){
    return [x,y];
}

f()//[1,undefined]
f(2)//[2,undefined]
f(,1)//报错
f(undefined,1)//[1,1]

//例二
function f(x,y=5,z){
      return [x,y,z];
}

f()//[undefined,5,undefined]
f(1)//[1,5,undefined]
f(1,,2)//报错
f(1,undefined,2)//[1,5,2]

上面代码中有默认值的参数都不是尾参数。
这时,无法只省略该参数,而不省略它后面的参数,除非显式输入undefined
如果传入undefined,将触发该参数等于默认值,null则没有这个效果

function foo(x=5,y=6){
    console.log(x,y);
}

foo(undefined,null)//5 null

你可能感兴趣的:(2019-02-18)