6>目前正在学习ES6,学到哪更新到哪里,都是基本的知识,复杂的目前还不会,涉及的代码都是亲自运行过的,若发现错误请指正。
三、变量的解构赋值
1、数组的解构赋值
解构:按照一定模式,从数组和对象中提取值,对变量进行赋值
基本用法
从数组中提取值,按照位置的对应关系对变量赋值,只要等号两边的模式相同,左边的变量就会被赋予对应的值
① 解构成功
let [A,,[[B]],...Z]=[1,2,[[3],4],5,6];
console.log(A);//1
console.log(B);//3
console.log(Z);//[5,6]
② 解构不成功,变量的值就等于undefined
let [x,y,...z]=['a'];
console.log(x);//'a'
console.log(y);//undefined
console.log(z);//[]
③ 不完全解构 ,等号左边的模式只匹配等号右边数组的一部分
let [a,[b],d]=[1,[2,3],4];
console.log(a);//1
console.log(b);//2
console.log(d);//4
④ 解构失败,等号的右边是不可遍历的结构,会报错
let [foo] =1; //undefined is not a function
let [foo] =false; //undefined is not a function
let [foo] =NaN; //undefined is not a function
let [foo] =undefined; //Cannot read property 'Symbol(Symbol.iterator)' of undefined
let [foo] =null; //Cannot read property 'Symbol(Symbol.iterator)' of null
let [foo] ={}; //undefined is not a function
默认值
解构赋值允许指定默认值
[x,y='b']=['a'];
console.log(x);//'a'
console.log(y);//'b'
ES6使用严格相等运算符(===)判断一个位置是否有值,所以,如果一个数组成员不严格等于undefined,默认值是不会生效的
var [x=1]=[undefined];
console.log(x);//1,严格等于undefined,默认值生效
var [x=1]=[null];
console.log(x);//null,不严格等于undefined,默认值不生效
如果默认值是一个表达式,那么这个表达式是惰性求值的,只有在用到的时候才会求值
例如下面的例子,因为x能取到值,所以函数 f 根本不会执行
function f(){
console.log('aaa');
}
const a = [1];
let [x=f()] = a;
console.log(x);//1
等价于:
function f(){
console.log('aaa');
}
const a = [1];
let x;
if(a[0]==undefined){
x=f();
}else{
x=a[0];
}
console.log(x);//1
默认值可以引用解构赋值的其他变量,但该变量必须已经声明
let [x=1,y=x] = [];//1,1
let [x=1,y=x] = [2];//2,2
let [x=1,y=x] = [1,2];//1,2
let [x=y,y=1] = [];//y is not defined, x 用到默认值 y 时,y 还没有声明
2、对象的解构赋值
对象的解构与数组有一个重要的不同,数组的元素是按照次序排列的,变量的取值由它的位置决定。对象的属性没有次序,变量只要与属性同名,就能取到正确的值。
① 变量与属性同名
var {bar,foo} = {foo:'aaa',bar:'bbb'};
console.log(foo);//'aaa'
console.log(bar);//'bbb'
var {hh} = {foo:'aaa',bar:'bbb'};
console.log(hh);//undefined,变量没有对应的同名属性,导致取不到值,等于undefined
② 变量名与属性名不一致
var {foo:hh} = {foo:'aaa',bar:'bbb'};
console.log(hh);//'aaa'
对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。
③ 重复声明
对于 let 和const 而言,变量不能重新声明,所以一旦赋值的变量以前声明过,就会报错
let hh;
let {foo:hh} = {foo:1};
console.log(hh);//Identifier 'hh' has already been declared标识符“hh”已经声明
如果没有第二个 let 命令,就不会报错
let hh;
({foo:hh} = {foo:1});
console.log(hh);//1
④ 嵌套赋值,模式不会被复制
var {loc:{start:{line}}} = node;
console.log(loc);//log is not defined
console.log(start);//start is not defined
console.log(line);
这里只有 line 是变量,loc 和 start 都是模式,不会被赋值
⑤ 对象解构指定默认值,默认值生效的条件是对象的属性值严格等于undefined。
var {x=3} = {x:undefined};
console.log(x);//3
var {x=3} = {x:null};
console.log(x);//null
var {foo} = {bar:'fff'};
console.log(foo);//undefined
var {foo:{bar}} = {fff:'fff'}//Cannot match against 'undefined' or 'null'.
⑥ 解构赋值已经声明的变量
var x;
{x}={x:1}
console.log(x);//Unexpected token =
报错原因是,js引擎会将{x}理解成一个代码块,从而发生语法错误。
正确的写法:不将大括号写在行首,避免js将其解释为代码块
var x;
({x}={x:1});
console.log(x);//1
3、字符串的解构赋值
字符串解构赋值时,字符串被转换成了一个类似数组的对象。
const [a,b,c,,d] = 'hello';
console.log(a);//a
console.log(d);//o
类似数组的对象都有length属性,因此还可以对这个属性解构赋值。
let {length} = 'hello';
console.log(length);
4、数值和布尔值的解构赋值
① 等号右边是数值或布尔值,先转化为对象再赋值
let {toString:s}=123;
console.log(s===Number.prototype.toString)//true
let {toString:s}=true;
console.log(s===Boolean.prototype.toString);//true
ps:不是很懂什么意思
② undefined 和 null 无法转为对象,解构赋值会报错
let {prop:x} = undefined;
//Cannot match against 'undefined' or 'null'.
let {prop:y} = null;
//Cannot match against 'undefined' or 'null'.
5、函数参数的解构赋值
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]
函数 move 的参数是一个对象,通过对这个对象进行解构,得到变量 x 和 y 的值。如果解构失败,则 x 和 y 等于默认值。
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]
函数 move 的参数指定默认值,而不是为变量 x 和 y 指定默认值
6、圆括号问题
不能使用圆括号的情况
① 变量声明语句中,模式不能带有圆括号
// Unexpected token (
var [(a)] = [1];
var {x:(c)} = {};
var {o:({p:p})} = {o:{p:2}};
② 函数参数中,模式不能带有圆括号,函数参数也属于变量声明
// Unexpected token (
function f([(z)]) {
return z;
}
console.log(f(2));
③ 不能将整个模式或嵌套模式中的一层放在圆括号中。
// Unexpected token (
//整个模式
({p:a}) = {p:42};
([a]) = [5];
//嵌套模式
[({p:a}),{x,c}] = [{},{}];
可以使用圆括号的情况
① 赋值语句的非模式部分可以使用圆括号
[(b)] = [3];
console.log(b);//3
({p:(d)} = {});
console.log(d);//undefined
[(parseInt.prop)] = [3];
console.log(parseInt.prop);//3
7、解构赋值的用途
(1)交换变量的值
[x,y] = [x,y];
(2)从函数返回多个数值
函数只能返回一个值,如果要返回多个值,只能将其放在数组或对象中返回
//返回一个数组
function example() {
return [1,2,3];
}
var [a,b,c] = example();
console.log([a, b, c]);//[1,2,3]
//返回一个对象
function example() {
return {
foo:1,
bar:2
};
}
var {foo,bar} = example();
console.log({foo,bar});//{foo:1,bar:2}
(3)函数参数的定义
(4)提取JSON数据
(5)函数参数的默认值
(6)遍历 Map 结构
部署了Iterator 接口的对象,都可以用 for … of 循环遍历。
·
补充:
Iterator
遍历器(Iterator)是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署Iterator接口,就可以完成遍历操作。
作用:
- 为各种数据结构,提供一个统一的、简便的访问接口
- 使得数据结构的成员能够按某种次序排列
ES6创造了一种新的遍历命令 for … of 循环,Iterator 接口主要供 for … of 使用
ES6中有三类数据结构原生具备Iterator接口:数组、某些类似数组的对象、Set 和 Map 结构