ES6 新规范中可以给函数的参数指定默认值。
浏览器支持:
//ES5 的一种变通方法
function run(x){
x = x === undefined ? 0 : x;
console.log(x)
}
run(); //0
run(false); //false
run(8); //8
//ES6 新增
function run(x=0){
console.log(x)
}
run(); //0
run(false); //false
run(8); //8
需要注意的几种情况:
//1.参数变量是默认声明的,所以不能用let或const再次声明。
function foo(x = 5) {
let x = 1; // 报错!
const x = 2; // 报错!
}
//2.使用参数默认值时,函数不能有同名参数。
function foo(x, x, y) { } // 不报错
function foo(x, x, y = 1) { } // 报错!
//3.参数默认值不是传值的,而是每次都重新计算默认值表达式的值。
let x = 99
function foo(p = x + 1) {
console.log(p);
}
foo(); // 100
x = 100;
foo(); // 101
//4.undefined 会触发该参数取默认值,null 则没有这个效果,
//并且设置了默认值的形参不能传空字符串参数。
function f(x, y = 5, z) {
return [x, y, z];
}
f(); // [undefined, 5, undefined]
f(1); // [1, 5, undefined]
f(1,undefined,null); // [1, 5, null]
f(1,,); // 报错!
f(1, ,2); // 报错!
//5.指定了默认值以后,函数的length属性,将返回没有指定默认值的参数个数。
//也就是说,指定了默认值后,length属性将失真。
(function(...args) {}).length // 0
(function (a) {}).length // 1
(function (a = 5) {}).length // 0
(function (a, b, c = 5) {}).length // 2
(function (a, b, c = 5, d) {}).length // 2 设置默认值后面的参数不再计入length
与解构赋值默认值结合使用:
//只有当函数 foo 的参数是一个对象时,变量x和y才会通过解构赋值生成
function foo({x, y = 5}) {
console.log(x, y);
}
foo({x: 1}); // 1 5
foo({x: 1, y: 2}); // 1 2
foo({}); // undefined 5
foo(); // 报错!
//下面代码指定,如果没有提供参数,函数foo的参数默认为一个空对象
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]
// 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]
参数的作用域
一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域(context)。
等到初始化结束,这个作用域就会消失。这种语法行为,在不设置参数默认值时,是不会出现的。
//下例中参数形成一个单独的作用域。y的值x指向第一个参数x,而不是全局变量x
var x = 1;
function f(x, y = x) { //相当于 let x; let y = x;
console.log(y);
}
f(2) // 2
---------------------------------------------------
let x = 1;
function f(y = x) { //相当于 let y = x;
let x = 2;
console.log(y);
}
f(); // 1
---------------------------------------------------
function f(y = a) { //相当于 let y = a;
let a = 2;
console.log(y);
}
f(); //全局变量a不存在,就会报错!
---------------------------------------------------
var x = 1;
function foo(x = x) { } //相当于 let x = x;
foo(); //由于暂时性死区,报错"x 未定义"
---------------------------------------------------
function bar(run = () => a) { //相当于 let run = a;
let a = 'inner';
console.log(run());
}
bar() // 报错: "a 未定义",参数中的a
---------------------------------------------------
var x = 1;
function foo(x, y = function() { x = 2; }) {
var x = 3;
y();
console.log(x);
}
foo() // 3
x // 1
/*
以上代码的作用域关系如下, 每一层中变量 x 都是不同的变量
{
var x = 3;
{
let x; let y = function(){ x = 2 }
{
var x = 3;
y(); //这里执行函数后,上级作用域中的 x 值为2
console.log(x);
}
}
}
如果将var x = 3的var去除,函数foo的内部变量x就指向匿名函数内部的 x,
那么做货输出就是2,而外层的全局变量x依然不受影响。
*/
应用:利用参数默认值,可以指定某一个参数不得省略,如果省略就抛出一个错误。
//下面代码的foo函数,如果调用的时候没有参数,就会调用默认值errMsg函数,从而抛出一个错误。
function errMsg() {
throw new Error('Missing parameter');
}
function foo(option = errMsg()) {
return option;
}
foo() //Error: Missing parameter
//另外,可以将参数默认值设为undefined,表明这个参数是可以省略的。
function foo(option = undefined) { ··· }
rest 参数
ES6 引入 rest 参数(形式为...变量名
),用于获取函数的多余参数,这样就不需要使用arguments对象了。
// arguments变量的写法
function sortNumbers() {
return Array.prototype.slice.call(arguments).sort();
}
// rest参数的写法
const sortNumbers = (...numbers) => numbers.sort();
/*
arguments 对象不是数组,而是一个类似数组的对象。使用Array.prototype.slice.call先将其转为数组。
rest 参数就不存在这个问题,它就是一个真正的数组,数组特有的方法都可以使用。
*/
//另一个 rest参数的写法 的例子
function add(...values) {
let sum = 0;
for (var val of values) {
sum += val;
}
return sum;
}
add(2, 5, 3) // 10
//下面是一个利用 rest 参数改写数组push方法的例子。
function push(array, ...items) {
items.forEach(function(item) {
array.push(item);
});
}
var a = [];
push(a, 1, 2, 3)
//注意,rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。
function f(a, ...b, c) { } // 报错!!!
//函数的length属性,不包括 rest 参数。
(function(a) {}).length // 1
(function(...a) {}).length // 0
(function(a, ...b) {}).length // 1
ES6对严格模式的修改,规定只要函数参数使用了默认值、解构赋值、或者扩展运算符,那么函数内部就不能显式设定为严格模式,否则会报错。
//从 ES5 开始,函数内部可以设定为严格模式。
function doSomething(a, b) {
'use strict';
// code
}
-----------------------------------------------------
//ES6 中会报错的几种情况
function doSomething(a, b = a) { // 报错
'use strict';
// code
}
const doSomething = function ({a, b}) { // 报错
'use strict';
// code
};
const doSomething = (...a) => { // 报错
'use strict';
// code
};
-----------------------------------------------------
//两种方法可以规避这种限制
//1.第一种是设定全局性的严格模式,这是合法的。
'use strict';
function doSomething(a, b = a) {
// code
}
//2.第二种是把函数包在一个无参数的立即执行函数里面。
const doSomething = (function () {
'use strict';
return function(value = 42) {
return value;
};
}());