我们在使用es6进行编程的过程中,经常出现编写函数需要传递参数默认值的情况。这其中又以对象默认传参用的最多,个人认为在函数中使用对象默认值参数有以下较明显的好处:
-
可以减少函数编写过程形参的个数,便于维护函数代码;
-
可以使用解构语法,很容易拿到对象参数里面对应的属性值;
-
有助于归类相关参数,提高可读性;
-
有助于将来函数的扩展,因为你可以给对象扩展很多属性,后续代码中,你可以通过读取相关属性值,就可以拿到所传参数。
我们先来通过一个简单的例子,来认识一下函数默认对象传参:
//es6
function student(person){
//解构相关参数值
let {name,age} = person;
console.log(name);//'小明'
console.log(age);//15
};
student({name:'小明',age:15});
如果用es5来实现上面的功能,代码如下:
//es5
function student(name,age){
var name = name;
var age = age;
console.log(name);//'小明'
console.log(age);//15
};
student('小明',15);
我们通过对比两种方法,可以明显看出es6的这种方法有如下:
-
参数少;
-
拿到相关参数值,更快捷、更方便;
-
含义是不是更形象、关联系更强?
当然了,实际应用中还有很多其它好处,可以自行google相关文档查看!
本文主要是说明,我们在使用对象默认参数编码过程中一些容易出现小错误的地方,我刚开始使用的时候,也是会经常弄错,本文就是把这些容易出错的地方找出来,并说明一下如何避免!
直接从一个代码例子说起吧,会足以说明每一步会遇到的问题。代码如下:
function student({name='xiaoming',age=15}){
let {name,age} = {name,age};
console.log(name);
console.log(age);
};
student();//我们期望分别打印name,age
但是我们发现上面的代码报错了,如:
Uncaught SyntaxError: Identifier 'name' has already been declared at :6:3
从上面提示我们可以看到name变量重复定义了,为什么呢?因为在用解构进行赋值的过程中,解构出现了跟es6中使用let、const定义变量一样出现“暂时性死区”、“变量不能重新定义”等问题!我们看一下其中一个代码片段:
let {name,age} = {name,age};
我们发现其实等号两边都同时进行了解构赋值,并且标记符使用了标记符let,我们知道在es6中,使用let进行定义的变量,在作用域块内不能重新定义,否则会报重复定义变量错误,上面的变量等价于:
let {name,age};
let {name,age};
//进行了两次声明
更形象地上面的代码,最终等价于下面的代码:
let name;
let age;
let name;
let age;
那怎么解决呢?其实有多种方法,我们先来看法1:
//使用var进行声明
//var允许变量重复定义
function student({name='xiaoming',age=15}){
var {name,age} = {name,age};
console.log(name);
console.log(age);
};
student();//我们期望分别打印name,age
再来看看法2:
//既然我们多写了一下,那为什么不少写一次呢?
//但是函数形参要改变一下
function student(person){
let {name,age} = person;
console.log(name);
console.log(age);
};
student();//我们期望分别打印name,age
再来看看法3:
//减少一次解构赋值
//相关取值也稍微做一下改变
function student({name='xiaoming',age=15}){
let person = {name,age};
console.log(person.name);
console.log(person.age);
};
student();//我们期望分别打印name,age
再来看看法4:
//减少一次解构赋值
function student({name='xiaoming',age=15}){
{name,age};
console.log(name);
console.log(age);
};
student();//我们期望分别打印name,age
虽然经过这样改动之后,不报重复定义变量的错误了,但是还是报错,我们来看一下错误信息:
Uncaught TypeError: Cannot destructure property `name` of 'undefined' or 'null'. at student (:2:23) at :6:3
很明显,错误信息提示我们不能从undefined和null中解构。因为我们在调用student函数的时候我们没有传实参,导致第一个参数是undefined,因此报错。要想解决这错误,我们根据提示就知道,只要保证第一个参数不是undefined或null就不会报错了,于是我们作如下修改:
function student(person={}){
let {name,age} = person;
console.log(name);
console.log(age);
};
student();//我们期望分别打印name,age
虽然现在我们的代码调用不会报错了,但是发现name,age打印出来都是undefined。这是因为我们虽然保证了第一个形参不是undefined或null,但是它是一个空对象,所以我们解构赋值的时候,得到其对应的name和age属性都是undefined,所以最后打印undefined了。怎么解决呢!好嘛,那我们一步一步来写,其实我们只要保证第一个形参不但不是undefined或者null,并且相应的属性值是对应的默认值就可以了,如此我们继续来写代码:
function student(person={name:'xiaoming',age:15}){
let {name,age} = person;
console.log(name);
console.log(age);
};
student();//我们期望分别打印name,age
如此,我们发现我们的代码能按我们预期那样工作了,这将更利于我们理解函数形参对象默认解构的含义了。我们发现下面的两段代码结果是相同的,它们真的是一样的吗?代码如下:
- 代码一:
function student({name='xiaoming',age=15}={}){
var {name,age} = {name,age};
console.log(name);
console.log(age);
};
student();//我们期望分别打印name,age
- 代码二:
function student(person={name:'xiaoming',age:15}){
var {name,age} = person;
console.log(name);
console.log(age);
};
student();//我们期望分别打印name,age
上面两个方法结果虽然一样,但是其实它们是有一丢丢区别的,首先从定义上:1、代码一说明函数的第一个参数默认是个空对象并在解构赋值给左边的对象时,如果name和age属性值为undefined,那么左边的对象的name和age属性值默认是xiaoming和15。代码二说明函数的第一个参数默认是{name:'xiaoming',age:15}!其实它们之间的区别和下面的两段代码类似:
//1
let {name='xiaoming',age='15'} = {};
//2
let {name,age} = {name:'xiaoming',age:15};
假如我们现在对代码一和代码二扩展如下:
- 代码一
function student({name='xiaoming',age=15}={}){
var {name,age,color} = {name,age,color};
console.log(name);
console.log(age);
console.log(color)
};
student({color:'red'});//我们期望分别打印name,age
- 代码二
function student(person={name:'xiaoming',age:15}){
var {name,age,color} = person;
console.log(name);
console.log(age);
console.log(color);
};
student({color:'red'});//我们期望分别打印name,age
我们发现代码一运行结果如下:
xiaoming 15 undefined undefined
而代码二运行结果如下:
undefined undefined red undefined
所以我们从输出结果上看出了两种写法的区别。主要区别如下:
1、第一种写法实际上限制了调用函数所传对象key的数量,传递其它未声明的key属性,在函数体就会拿不到,为undefined。第二种方法实际上应该首先理解为如果调用函数的时候不传对象参数,那么其默认值就是就是默认对象,如果传递了一个新对象,那么就会以这个新传递的对象作为函数体内解构赋值对象;
2、两种写法的解构对象都是函数形参对象等号左边的那个值;