最近学习es6,学习到解构赋值的时候,遇到下面这样一个例子(例1):
function move({x = 0, y = 0} = {}) {
return [x, y];
}
console.log(move({x: 3, y: 8}));// [3, 8]
console.log(move({x: 3}));// [3, 0]
console.log(move({}));// [0, 0]
console.log(move());// [0, 0]
我试着理解它,并且觉得自己已经理解了,直到我又看到下面这个例子(例2),让我完全推翻了自己的认识,
function move({x, y} = { x: 0, y: 0 }) {
return [x, y];
}
console.log(move({x: 3, y: 8})); // [3, 8]
console.log(move({x: 3})); // [3, undefined]
console.log(move({})); // [undefined, undefined]
console.log(move()); // [0, 0]
es6给出的解释是这样的:例子1是给变量x,y指定默认值,例子2是给函数的参数指定默认值,这是什么意思呢?下面我说明一下自己的理解,希望帮助大家理解,如果有错误,欢迎指正~
经过抽丝剥茧之后,上面的例子实际上是一个对函数参数进行解构赋值的应用实例,因为涉及到了两个默认值(函数参数的默认值和解构赋值的默认值),所以给人一种乱花渐欲迷人眼的感觉。首先,简单说明一下基本的对象解构赋值操作中解构赋值默认值的使用:
let { x = 0, y = 0 } = { x: 4, y: 5 };
console.log(x);//4
console.log(y);//5
在简单的解构赋值操作中,等号左边的对象是变量组成的,我在这里称它左变量(为了后文方便理解,姑且这样定义)即:x,y,等号右边的部分是真正的值,这里称它为右真值;而左变量中x,y等号右边的部分分别为变量x和变量y的默认值。它们之间的关系是:当右侧x,y没有指定值的时候(也就是x===undefined和y===undefined的时候),变量的默认值会生效,而当x,y指定了值的时候,变量的默认值不会生效。那么什么是衡量x,y是否===undefined的标准呢?可以是以下几种情况:
1.右侧没有对应的属性
let { x = 0, y = 0 } = { y: 5 };//这种情况下属性x就是undefined,那么此时变量的默认值生效,所以输出:x为0,y为5
2.右侧属性值就是undefined
let { x = 0, y = 0 } = { x:undefined,y: 5 };
值得特别说明的是:这里判断默认值是否生效的是右真值是否===undefined,而不是==undefined,两者有什么区别呢?在js中undefined==null是成立的,而undefined===null是不成立的。关于这一点可以通过在浏览器中输入下面的代码验证:
console.log(undefined===null);//false
console.log(undefined==null);//true
所以,这里得到这样的结论:
let { x = 0, y = 0 } = { x:null,y: 5 };
console.log(x);//null
console.log(y);//5
也就是说,undefined===null是false,所以默认值不能生效,所以最终结果是null。
现在,再说明一下函数参数默认值,如下:
function foo(a = 0){...}
foo();//这里函数调用时,没有传实参,所以默认值0生效;
foo(10);//这里传递了实参,所以实参是10;
所以,函数参数默认值生效的标准是函数调用时没有传递实参;
了解了解构赋值和函数默认值之后,最初提出的问题就可以认为是在解决下面几个问题:
步骤1.函数调用过程中有没有传递参数?如果传递了参数那么实际上就是要为传递的实参进行解构赋值,如果没有传递参数那么就是要为函数参数的默认值进行解构赋值;所以还原到基本的解构赋值模型中,就是实参或者函数参数默认值为解构模型中的右真值,具体右真值是实参还是函数参数默认值是由函数调用有没有传递实参决定的,而变量的对象或数组是解构模型中的左变量;
步骤2.确定了谁是解构赋值的对象之后,再看实参或者函数参数默认值有没有解构左变量中对应的属性,没有那么解构赋值的默认值生效,否则不生效;
下面分析一下上面的两个例子:
注释:下面传参时move({})
中的{}
,将替换function move({x, y} = { x: 0, y: 0 })
中的 { x: 0, y: 0 }
,因为传递参数{}
给了一个新的引用地址不再指向function move({x, y} = { x: 0, y: 0 })
中的{ x: 0, y: 0 }
例1:
function move({x = 0, y = 0} = {}) {
return [x, y];
}
console.log(move({x: 3, y: 8}));// [3, 8]<br><br>//说明:传递了实参,所以函数默认值{}不生效,对实参{x: 3, y: 8}解构赋值,对应的属性都有值,所以解构赋值的默认值不生效,所以x = 3,y = 8;
console.log(move({x: 3}));// [3, 0]
//说明:传递了实参,所以函数默认值{}不生效,对实参{x: 3}解构赋值,对应的属性x有值,y没有值,所以解构赋值的默认值y生效,x不生效,所以x = 3,y = 0;
console.log(move({}));// [0, 0]
//说明:传递了实参{},所以函数默认值{}不生效,对实参{}解构赋值,对应的属性都没有值,所以解构赋值的默认值生效,所以x = 0,y = 0;
console.log(move());// [0, 0]
//说明:没有传递实参,所以函数默认值{}生效,对实参{}解构赋值,对应的属性都没有值,所以解构赋值的默认值生效,所以x = 0,y = 0;
例2:
function move({x, y} = { x: 0, y: 0 }) {
return [x, y];
}
console.log(move({x: 3, y: 8})); // [3, 8]
//说明:传递了实参,所以函数默认值{x: 0, y: 0}不生效,对实参{x: 3, y: 8}解构赋值,对应的属性都有值,所以解构赋值的默认值不生效,所以x = 3,y = 8;
console.log(move({x: 3})); // [3, undefined]
//说明:传递了实参,所以函数默认值{x: 0, y: 0}不生效,对实参{x: 3}解构赋值,对应的属性x有值,y没有值,所以x=3,因为本例没有解构赋值的默认值,所以y就是undefined;
console.log(move({})); // [undefined, undefined]
//说明:传递了实参{},所以函数默认值{x: 0, y: 0}不生效,对实参{}解构赋值,对应的属性没有值,所以x,y就是undefined;
console.log(move()); // [0, 0]
//说明:没有传递实参,所以函数默认值{x: 0, y: 0}生效,对实参{x: 0, y: 0}解构赋值,对应的属性有值,所以x,y就是0;