函数参数的默认值
基本用法
在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
复制代码
以上写法会有一个问题,就是当我们传进去一个y值,但是其对应的布尔值为false,例如 log('hello','')
,结果被修改为了默认值! 所以,为了避免这个问题,我们替换如下语句
if(typeof 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
复制代码
有一点需要注意 参数默认值不是传值的,而是每次重新计算默认值表达式的值,参数默认值是“惰性求值的”
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
复制代码
注意:参数变量是默认声明的,在函数体中,不能再用 let const 进行声明,也 不允许有同名参数。
结合解构
function foo({x, y = 5}){
console.log(x, y)
}
foo({}) // undefined 5
foo({x:1}) // 1, 5
foo({x: 1, y: 3}) // 1, 3
foo() //报错
复制代码
只有函数的参数是一个对象时,变量x y 才会通过解构赋值生成。如果函数调用时,参数不是对象,变量x y 就不会生成,会报错。
在前面文章 重学ES6 解构我们他提到过函数参数的默认值,jQuery ajax的解构
这里再用一个新的API写一遍
function fetch(url,{body = '', metthod = 'GET', headers = {}}){
console.log( method )
}
fetch('http://example.com', {}) // GET
fetch('http://example.com') // 报错
复制代码
这个写法不能省略第二个参数~那就太不好了。。。所以,我们要再结合函数的默认值,就可以省略第二个参数了
function fetch(url,{body = '', metthod = 'GET', headers = {}} = {}){
console.log( method )
}
fetch('http://example.com', {}) // GET
fetch('http://example.com') // GET
复制代码
注意:在函数传参解构中,只要传入实际参数,那么就拿传入的实际参数进行解构,如果没有传入实际参数,且参数写了默认值,函数参数得到值是 undefined 时,被赋予默认值。
// demo 1
function m1 ({x = 0,y = 0} = {}){
return [x, y]
}
// demo 2
function m2 ({x, y} = {x: 0, y: 0}){
return [x, y]
}
//x y 都无值
m1({}) //[0, 0]
m2({}) // 没有写默认值,对实参进行解构,[undefined, undefined]
m1({z: 3}) // [0, 0]
m2({z: 3}) // [undefined, undefined]
复制代码
默认参数位置
通常,定义了默认值的参数应该是函数的尾参数。这样可以看出到底省略了哪些参数。
function f(x, y, z = 1){
return [x, y, z]
}
f() // [undefined, undefined, 1]
复制代码
函数的 length
指定了默认值以后,函数的 length 属性将返回没有指定默认值的参数个数。指定了默认值后,length属性将失真
(function (a) {}).length //1
(function (a = 5)).length // 0
(function (b ,c ,a = 5)).length // 2
复制代码
length 属性 含义是 “函数预期传入的参 数个数” ,某个参数指定默认值以后,函数传入参数个数就不包含这个了,rest参数也不会计入length属性
作用域
设置了参数的默认值后,函数进行初始化时,参数会形成一个单独的作用域。等到初始化结束,这个作用域就会消失。这种语法行为在不设置参数时是不会出现的。
var x= 1
function f(x, y = x){ //相当于 let x
console.log(y)
}
f(2)
复制代码
// 如果此时全局 x 不存在,就报错了
function f(y = x){ //相当于 let y = x
console.log(y)
}
f() // x is nit defined
复制代码
var x = 1
function f(x = x){ //相当于 let x = x 暂时性死区
console.log(x)
}
f() // x is nit defined
复制代码
如果参数是一个函数
let foo = 'ourter'
function bar (func = () => foo){ //在这里,函数里面的foo没有定义,所以foo指向外层的 foo
let foo = 'inner'
console.log(func())
}
bar()
复制代码
又一个例子
此例子,x共有3个,全局作用域下 foo函数参数作用域 还有 foo函数内部作用域,共三个,互不相同
var x =1
function foo(x, y = function() { x = 2}){
var x = 3
y()
console.log(x)
}
foo() // 3
x // 1
复制代码
但是,如果去掉 foo 函数 内部的 var x = 3
的 var,那么就剩下两个不同的x,即:全局下的x 和 函数 foo 参数作用域下的x ,因为函数内部的 x 和 函数参数的x 已经是同一个x
var x =1
function foo(x, y = function() { x = 2}){
x = 3
y()
console.log(x)
}
foo() // 2
x // 1
复制代码
两个例子都不会影响外部全局变量x的值。
应用
参数默认值,可以指定某一个参数不能省略,如果省略了,就跑出一个错误
function throwIfMissing(){
throw new Error('Missing parameter')
}
function foo(mustBeProvided = throwIfMissing()){
return mustBeProvided
}
foo()
复制代码
rest 参数
es6 引入了 rest参数,(形式为 ...变量名),用于获取函数的多余参数,也就不需要使用 arguments 参数对象了。 rest参数搭配的变量是一个数组,该变量将多余的参数放入其中。
function add(...values){
let sum = 0
for(var val of values){
sum += val
}
return sum
}
add(1,2,3) // 6
复制代码
几个例子
之前,用arguments的写法
// Array.prototype.slice.call(arguments) 将参数转化为数组
// array.sort
function sortNumbers() {
return Array.prototype.slice.call(arguments).sort()
}
// rest 参数的写法
//...numbers 将numbers 转化为数组
const sortNumbers = (...numbers) => numbers.sort()
复制代码
rest参数改写 push
function push(array,...items){
items.forEach(function(item){
array.push(item)
})
}
var a = []
push(a,1,2,3)
复制代码
rest 参数之后,不能再有其他参数,所以,rest只能是最后一个参数。而且,函数的length,不包括rest参数。