目录
将函数默认值设为undefined,表明这个参数是可以省略的:
ES6以往文章
ES6标准---【一】【学习ES6看这一篇就够了!!】-CSDN博客
ES6标准---【二】【学习ES6看这一篇就够了!!】-CSDN博客
ES6之前函数默认值参数的处理方法
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(如0),则该赋值不起作用
就像上面代码最后一行,参数y等于空字符,结果被改为默认值
ES6函数参数的默认值
ES6允许为函数的参数设置默认值,即直接写在参数定义的后面
function log(x, y = 'World') {
console.log(x, y);
}
log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello
参数变量是默认声明的,所以不能用“let”或“const”再次声明
function foo(x = 5) {
let x = 1; // error
const x = 2; // error
}
但是使用“var”可以再次声明
function foo(x = 5){
console.log(x);
var x = 1;
console.log(x);
}
foo();
- 使用函数默认值时,函数不能有同名参数
- 不使用函数默认值,函数可以有同名参数
<body>
<script>
//foo函数不报错
function foo(x,x,z){
return 1;
}
//bar函数报错,因为参数x重复了
function bar(x,x=1){
return 2;
}
</script>
</body>
- 如果有同名参数,则最后一个参数是会被正确读入
<body>
<script>
//foo函数不报错
function foo(x,x,z){
console.log(x,x,z);
}
foo(1,2,3);
</script>
</body>
效果:
参数默认值不是传值的,而是每次都重新计算默认值表达式的值
即:“参数默认值是惰性求值的”
let x = 99;
function foo(p = x + 1) {
console.log(p);
}
foo() // 100
x = 100;
foo() // 101
与结构赋值默认值结合使用
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
理解“解构赋值默认值”和“函数参数默认值”:
// 写法一
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]
//参数都有值时
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({y:8}); //[0,8]
m2({y:8}); //[undefined,8]
//x,y都无值时:
m1({}); //[0,0]
m2({}); //[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]
函数的length属性
函数的length属性将返回“没有默认值的参数个数”
(function (a) {}).length // 1
(function (a = 5) {}).length // 0
(function (a, b, c = 5) {}).length // 2
- 如果“默认值参数”不在“参数列表”最后,那么length属性也不会计算后面的参数:
(function (a = 0, b, c) {}).length // 0
(function (a, b = 1, c) {}).length // 1
作用域
一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域(context)
等到初始化结束,这个作用域就会消失
var x = 1;
function f(x, y = x) {
console.log(y);
}
f(2) // 2
let x = 1;
function foo(y = x) {
let x = 2;
console.log(y);
}
foo() // 1
- 调用函数f时,参数形成一个单独的作用域,y的默认值等于参数列表中的x的值(2)
- 调用函数foo时,参数形成一个单独的作用域,y的默认值等于x的值,由于当前作用域没有x,转而在“函数所在作用域”中寻找x(1)
下面这样写,也会报错(由于暂时性死区,会报错“x未定义”):
var x = 1;
function foo(x = x) {
// ...
}
foo() // ReferenceError: x is not defined
参数的默认值是一个函数
如果参数的默认值是一个函数,该函数的作用域也遵守“单独形成一个作用域”的规则
let foo = 'outer';
function bar(func = () => foo) {
let foo = 'inner';
console.log(func());
}
bar(); // outer
如果写成下面这样,就会报错:
function bar(func = () => foo) {
let foo = 'inner';
console.log(func());
}
bar() // ReferenceError: foo is not defined
正确理解函数默认值的例子
var x = 1;
function foo(x, y = function() { x = 2; }) {
var x = 3;
y();
console.log(x);
}
foo() // 3
x // 1
函数foo的参数形成一个单独作用域
在这个作用域里,y的默认值是一个匿名函数,函数内部的变量x指向同一个作用域的第一个参数x
函数foo内又声明一个内部变量x,该变量与第一个参数x由于不是同一个作用域,所以不是同一个变量,因此执行y后,内部变量x和全局变量x的值都没变
应用
指定某一个函数参数不得省略,如果省略就抛出一个错误
<body>
<script>
function throwError() {
throw new Error('Something went wrong');
}
function foo(mustParams=throwError()){
console.log(mustParams);
}
foo();
</script>
</body>
效果:
将函数默认值设为undefined,表明这个参数是可以省略的:
function foo(optional = undefined) { ··· }