今天我们来聊下ES6中的解构赋值和扩展运算符,它们在原生js开发、vue开发、小程序开发等等应用非常广泛!目前浏览器已经部分支持ES6的语法特性,当然用于线上的时候还是需要工具转换成es5,但是可以预见未来浏览器肯定是全面支持es6,(现在高版本浏览器已经支持es6语法)—————— IE:都看我干嘛!!!
直接进入正题*********************************************************************************************************************************
数组解构
let [a,b,c] = [1,2,3]
1、本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。
let [x,...y] = [1,2,3,4]
x //1
y //[2,3,4]
let [foo] = []
foo //undefined
结构不成功返回:undefined
如果等号右边不是数组,或不是可遍历的结构(是否有iterator接口),将报错!
let [a] = 1;
let [a] = false;
let [a] = NaN;
let [a] = null;
let [a] = undefined;
let [a] = {};
上边前五个转化为对象,不具备iterator接口,最后一个本身不具备iterator接口。。。
总结:只要具备Iterator接口的数据结构,都可采用数组形式进行解构赋值。
2、默认值:
解构赋值允许使用默认值:
let [foo = 1] = []
foo // 1
对象的解构赋值
1、解构赋值,不仅可以用于数组,还可以用于对像。
但是值得注意的是:数组的元素是按照顺序排列的,变量取值是按照顺序依次取值;而对象不同,对象属性没有顺序,左边的变量名必须和右边的属性对应,才能取到。。。解构失败,变量值为undefined。。。
var { name,age } = {name:"lxc",age:"20"}
console.log( name + "," + age)//lxc , 20
var { foo } = { name:"lxc" , age:"20" }
console.log( foo ) // undefined
2、将现有的(原生的)对象方法,赋值给一个变量,用起来会方便的多。。。
var { random,floor,ceil } = Math;
console.log(random())//产生一个随机数
var { log } = console;
log("hehe") //hehe
3、变量名可与属性名不一致,前提需要把变量名赋值给另一个变量
var { name:alias } = { name:"lxc" };
console.log(alias) // lxc
4、默认值:对象的解构也可以指定默认值。
var { x = 1 } = {};
x // 1
var { x = 1, y = 2 } = {x:3}
x //3
y //2
var { x:xx = 1 } = {x:2}
xx // 2
5、使用默认值的前提是 右边的值绝对等于undefined的时候,因为ES6是严格模式
var { x = 1 } = { x:undefined }
x // 1
var { x = 1 } = { x : null }
x // null 因为null与undefined不严格相等,所以不会取默认值
函数参数的解构赋值
function test([x,y]){
console.log(x +"|" + y) //1 | 2
}
test([1,2])
上边代码:函数test的参数表面上市一个数组,但传入参数的那一刻,已被解构成单个数据x和y。。。
解构赋值的应用:
1、交换变量
let x = 1;
let y = 2;
[x,y] = [y,x]
2、从函数中返回多个值
function test(){
var arr = [1,2,3,4];
return arr;
}
var [a,b,c,d] = test()
console.log(a,b,c,d) //1 2 3 4
//或者用扩展运算符
var a = test()
console.log(...a) //1 2 3 4
3、提取JSON数据:解构赋值对于提取后台返给前台的数据尤为方便
var json = {
id:2,
status:"ok",
data:[{name:"lxc",age:20}]
}
var { id,status,data } = json;
console.log(id,status,data) // 2 "ok" [{name:"lxc",age:20}]
4、遍历Map结构:用解构赋值来取Map结构中的属性和值非常方便
const map = new Map();
map.set("name","lxc");
map.set("age",20)
for([property,value] of map){
console.log(property)//name age
console.log(value)//"lxc" 20
}
扩展运算符
一、在形参和实参中的使用
1、在形参中,使用扩展运算符,它会把实参统一收集成一个数组;
2、在实参中使用扩展运算符,它会把一个数组扩展开;
例如下边例子:
随机生成一个数组,在调用sum时,会把数组扩展开,在sum形参中,又会把每个元素放到一个数组中去。
//求和函数
function sum(...arr){
let sum = 0
for(let i = 0 ; i < arr.length ; i++){
sum += arr[i]
}
return sum
}
// 给定一个长度,生成一个随机数的数组
function generateArr(length){
const ARRAY= []
for(let i = 0 ; i <length ; i ++){
ARRAY.push(Math.floor(Math.random()*11))
}
return ARRAY
}
let result = sum(...generateArr(10))
console.log(result) //随机产生一个10个数相加的和
二、数组扩展运算符注意事项:
1、展开功能(把一个数组扩展开)
console.log( ...[1,2,3] ) //1 2 3
[...document.getElementByClassName(div)] //[ div , div , div ]把类数组转化为数组
function test(x,y){
console.log(x+y)//3
}
test(...[1,2])
只有函数调用时,扩展运算符才可以放到圆括号中,以下几种情况会报错,因为扩展运算符的括号不是函数调用:
console.log((...[1,2]))//Uncaught SyntaxError: Unexpected number
(...[1,2,3]) //Uncaught SyntaxError: Unexpected number
2、替代ES5的写法:ES6写法简单、明了
//ES5
function test(x,y,z){}
let arr = [1,2,3];
test.apply(null,arr)
//ES6
let test = (x,y,z)=>{}
let arr = [1,2,3];
test(...arr)
我们经常会看到的一个例子求数组最大值:(看到ES6的用法,从此与ES5断绝来往!)
//ES5
var arr = [1,2,3,4];
console.log( Math.max.apply(null,arr) ) //4
//ES6
let arr = [1,2,3,4];
console.log( Math.max(...arr) ) // 4
3、数组的合并
var arr1 = [1,2];
var arr2 = [3,4];
// ES5
[].push.apply(arr1,arr2)
console.log(arr1)//[1,2,3,4]
// ES6,
console.log([...arr1,...arr2]) //[1,2,3,4]
三、展开运算符应用
<1> 数组的复制(深层克隆)——以下是ES5和ES6的深层克隆数组
// ES5
var arr = [1,2];
var newArr = arr.concat()
newArr[0] = 0;
console.log(arr) //[1,2]
// ES6
var arr = [1,2];
var newArr = [...arr]
newArr[0] = 0;
console.log(arr) //[1,2]
<2> 字符串转真正数组
var str = "hello";
console.log([...str]) //["h","e","l","l","o"]
<3> 任何定义了遍历器(Iterator)接口的对象,都可以用扩展运算符转为真正数组。
对于没有Iterator的数据结构,可以用Array.from方法将类数组转化为真正数组。。。。
//下边nodeList是一个类数组,扩展运算符可以转为真正数组,原因在于nodeList实现了Iterator
let nodeList = document.getElementByClassName("div");
let newArr = [...nodeList]
对象扩展运算符
ES8引入了对对象的扩展:
1、解构赋值和扩展运算符一起用
let { ...obj } = { a:1,b:2,c:3 }
console.log(obj) //{a:1,b:2,c:3}
let {a,b,...c} = { a:1,b:2,c:3,d:4 }
console.log(a) //1
console.log(b) //2
console.log(c) //{c:3,d:4}
值得注意的是:上边的写法复制出来的对象是深层拷贝
let oldObj = {a:1,b:2,c:3};
let { ...x } = oldObj;
x.a = 0;
console.log(oldObj)// {a:1,b:2,c:3}
解构赋值必须是最后一个参数,否则报错
let { ...a,b,c } = { a:1,b:2,c:3 }//报错
2、对象的扩展运算符等同于使用了Object.assign()方法
let clone = {...a};
// 等同于
let clone = Object.assign({},a)
3、合并两个对象
let obj = {...a,...b}
//等同于
let obj = Object.assign({},a,b)
补充:
还是关于对象克隆的问题,二维的对象,使用展开运算符进行克隆时,里边的对象还是浅克隆的对象:
其实原因很简单,在克隆外层对象的时候,里边的对象你没有给它展开,obj[address] 和newArr[address] 指向的是同一个地址!
let obj = {
name:'lxc',
age:20,
address:{
city:'yt'
}
}
let newObj = {...obj}
console.log(newObj['address'] === obj['address']) //true
解决方法
也很简单,在新对象里创建一个新address属性,值是一个对象,把原来address对象扩展开:
实际上,利用的就是对象属性的唯一性原则,重复创建的属性,后边的会覆盖前边的。。。
let obj = {
name:'lxc',
age:20,
address:{
city:'yt'
}
}
let newObj = {
...obj,
address:{
...obj['address']
}
}
console.log(newObj['address'] === obj['address']) //false