年都过去半个月了,我终于又从新开始更新了。虽然只是第二篇,可是我会继续加油努力,必定不会放弃更新的。在文章中如有什么不妥或者您有更多建议的话,欢迎和期待您给我留言,您的每个留言均可能成为我进步的助力,十分感谢。那就废话很少说直接开始吧。es6
解构赋值,顾名思义,就是解开一个结构,而后从里面拿出值用来给变量赋值赋值。因此解构赋值主要是以数据类型来划分的。ajax
var [a,b,c] = [1,2,3];
复制代码
上述代码算是最简单的数组解构赋值,其实也能够看作是数据的另外一种展现。好比上述代码与下面的代码实际上是同样的。json
var a=1,b=2,c=3;
复制代码
因此解构赋值最主要的做用,是可让咱们简化提取值的过程。数组
本质上,这种写法属于“模式匹配”,同模式下,左边的变量就会被赋予对应位置的右边的值。例如:bash
let [a,[[b],c]] = [1,[[2],3]];
a //1
b //2
c //3
复制代码
而且只要模式相同,即使部分位置的变量或者值为空,依旧能够进行匹配。数据结构
let [a, , b] = [1,2,3];
a //1
b //3
let [a,b,c] = [1,2];
a//1
b//2
c//undefind
复制代码
当解构不成功,即变量没有获得赋值,或者直接赋值undefind
时,变量的值就会等于undefind
。async
当匹配两边的模式相同,且长度不一样时,此时的解构赋值被称为不彻底解构。虽然叫不彻底解构,可是依旧算解构成功的。函数
let [a,b] = [1,2,3];
a //1
b //2
复制代码
上面一直提到一个前提状况,那就是模式相同,没错,这是比较须要注意的一点。当两边模式不一样时,解构赋值是会报错的。ui
let [a] = 1;
let [b] = false;
let [c] = {};
复制代码
在不严谨的状况下,咱们能够说,当两边的数据类型不一样时,解构赋值会出现报错。url
只要某种数据结构具备Iterator接口,均可以采用数组形式的解构赋值。Iterator接口最主要的功能是能够提供遍历命令for...of
使用,不难猜想,其实数组解构赋值是一个将变量遍历循环,而后一一进行赋值的操做。
function* fibs() {
let a = 0;
let b = 1;
while (true) {
yield a;
[a, b] = [b, a + b];
}
}
let [first, second, third, fourth, fifth, sixth] = fibs();
sixth // 5
复制代码
以上是阮一峰大神ES6入门里面的实例,因为我的目前还不清楚具体哪些数据结构具备Iterator
接口,因此这里直接搬运一下。
let [a=1] = [];
a//1
复制代码
解构赋值操做时,能够设置一个默认值,若解构赋值操做室,对应位置上的值为undefind
时,将会给变量赋值默认值。
须要注意的是,ES6内部使用严格相等运算符(===)来判断一个位置是否有值,我通常习惯称它为全等符号。因此,与通常的判断不一样,这里只有用于赋值的数组成员的值为
undefined
(严格等于undefined
)时,默认值才会生效。
let [a=1] = [undefined];
a//1
let [b=1] = [null];
b//null
let [c=1] = [NaN];
c//NaN
复制代码
使用默认值时,还能够引用解构赋值的其余变量,但前提是该变量已声明。
let [a=1,b=a] = [];
a//1
b//1
let [a=b,b=1] = [];
//ReferenceError: b is not defined
var [a=b,b=1] = [];
a//undefined
b//1
复制代码
let { a,b } = { a:'1',b:'2'};
a //'1'
b //'2'
复制代码
对象的解构赋值与数组的解构赋值最大的不一样之处,在于数组的解构赋值,变量的取值是由位置决定的;而对象的解构赋值,变量的取值是由属性名来决定的,只有变量与属性名相同,才能够取到值。
let { a , b } = { b : '2' , a : '1' };
a //1
b //2
let { a } = { b: '1' , c: '2' };
a//undefined
复制代码
当变量名与属性名不一致,却又须要进行解构赋值时,可使用变量再进行一次解构赋值。
let obj = { a : '1' , b : '2' };
let { a : c , b : d } = obj;
c //'1'
d //'2'
复制代码
而且,在这过程当中,实际被赋值的,实际上是c
和d
。而a
和b
是模式,起到相似于一个中介做用,不会被实际赋值。
let { a : b } = { a : '1'};
a //ReferenceError: a is not defined
b //'1'
复制代码
对象的解构赋值,与数组的解构赋值同样,也能够用于嵌套结构的对象。
let a = {
b : [
'1',
{ c : '2' }
]
};
let {
b : [
x ,
{ c }
]
} = a;
x // '1'
c // '2'
b // ReferenceError: b is not defined
复制代码
此时b
只是模式,因此没法被赋值。
对象的解构赋值也有默认值,默认值的设置方式与数组相同,而不是依旧使用对象的内部写法。
let { a = 1} = {};
a // 1
let { b : 1 } = {};
//SyntaxError: Invalid destructuring assignment target
复制代码
默认值生效的条件与数组的解构赋值相同,属性值必须严格等于undefined
才会生效。
let { a = 1 } = { a : undefined };
a //1
let { b = 1 } = { b : null };
b // null
let { c = 1 } = { c : NaN };
c // NaN
复制代码
在对嵌套的对象使用解构赋值时,须要注意,若子对象所在的父属性不存在时,会报错。这也是我在工做中,发现比较常见的一种报错,仍是须要多多注意的。特别是在使用多层结构的时候,例如res.data.id
。
let { a: {b} } = { c : '1' };
TypeError: Cannot destructure property `b` of 'undefined' or 'null'
复制代码
在使用对象解构赋值的时候,若是要对已经声明的变量进行解构赋值,须要当心。
let a;
{a} = {a:1};
//SyntaxError: Unexpected token =
复制代码
这里是由于JavaScript引擎会将{a}
当作一个代码块,从而引起语法错误。因此须要避免将大括号写在行首。
let a;
({a} = {a:1});
a // 1
复制代码
因为数组的本质是特殊的对象,所以能够对数组进行对象属性的解构赋值。
let a = [1, 2, 3];
let {0 : b, 2: c} = a;
b // 1
c // 3
复制代码
第二行代码的0和2表明的是数组的位置,能够简单理解为如下代码:
let a = [1,2,3];
let b = a[0];
let c = a[2];
复制代码
字符串也能够进行解构赋值,由于此时字符串被转换成了一个相似数组的对象。
let [a, b, c, d, e] = 'hello';
a // 'h'
b // 'e'
c // 'l'
d // 'l'
e // 'o'
复制代码
看到这里是否是感受这个过程有点眼熟,其实这个过程能够理解为如下代码:
let x = 'hello';
a = x[0];
b = x[1];
c = x[2];
...
复制代码
解构赋值时,若是等号右边是数值或者布尔值,则会先转化成对象。
let {toString:a} = 123;
a === Number.prototype.toString // true
let {toString:a} = 123;
a === Boolean.prototype.toString // true
复制代码
解构赋值的规则是,若等号右边的值不是对象或者数组,就会先将其转化成对象。因为
undefined
和null
没法转化成对象,因此对其进行解构赋值时会报错。
let { a:b } = undefined;
//TypeError: Cannot destructure property `a` of 'undefined' or 'null'
let { a:b } = null;
//TypeError: Cannot destructure property `a` of 'undefined' or 'null'
复制代码
function add({x,y]){
return x+y;
}
add({1,2}); //3
复制代码
函数add
的参数表面上为一个数组,可是在传入参数的那一刻,数组参数就被解构成了2个变量,x
和y
。
函数参数的解构也能够用默认值。
function move({x=0,y=0} = {} ) {
return [x,y];
}
move({x:1}); // [1,0];
move({}); // [0,0];
复制代码
函数参数的解构有另外一种写法,会得出另外一种结果。
function move({x,y} = {x:0,y:0} ) {
return [x,y];
}
move({x:1}); // [1,undefined];
move({}); // [undefined,undefined];
move(); // [0,0]
复制代码
上述的代码时为move
函数参数设置默认值,而不是为解构后的x
和y
设置默认值,因此会得出不同的结果。
在使用解构赋值的时候,圆括号是否使用,是一个问题。
ES6的规则中说明,只要有可能致使解构歧义的,就不能使用圆括号。
但因为该规则的标准不容易衡量和辨别,因此通常是尽可能不使用圆括号。
变量声明语句
let [(a)] = [1];
let {x: (c)} = {};
//上述两句代码显示为undefined
let ({x: c}) = {};
let {(x: c)} = {};
let {(x): c} = {};
let { o: ({ p: p }) } = { o: { p: 2 } };
//上述四句代码会报错。
复制代码
上述代码发生这种状况,主要是由于它们都是变量声明语句,模式不能使用圆括号。
函数参数 函数参数也属于变量声明,所以不能带圆括号。
function a( [ ( b ) ] ) { return c; }
复制代码
赋值语句的模式
( { a:b } ) = { a:1 };
[ ({a:b}) , { c:d } ] = [{},{}];
复制代码
不管是将整个模式放入圆括号中,仍是将部分模式放入圆括号中,都会致使报错。
交换变量的值
let a = 1;
let b = 2;
[a,b] = [b,a]
复制代码
从函数返回多个值
经过解构赋值,能够很方便的从数组或者对象里获取多个返回值。
function arr(){
return [1,2,3];
}
let [a,b,c] = arr();
//返回一个数组
function arr() {
return {
a:1,
b:2
};
}
let { a,b } = arr();
复制代码
函数参数的定义 解构赋值能够方便地将一组参数与变量名对应起来。
// 参数是一组有次序的值
function f([a, b, c]) { ... }f([1, 2, 3]);
// 参数是一组无次序的值
function f({a, b, c}) { ... }f({z: 3, y: 2, x: 1});
复制代码
提取JSON数据 在提取JSON对象中的数据时,解构赋值能起到很是简便和快速的做用,使得代码更加简洁。
let jsonData = {
id: 42,
status: "OK",
data: [867, 5309]};
let { id, status, data: number } = jsonData;
console.log(id, status, number);
复制代码
函数参数的默认值 经过使用解构赋值,在给函数参数赋予默认值时,整个代码会显得更加简洁。
jQuery.ajax = function (url, {
async = true,
beforeSend = function () {},
cache = true,
complete = function () {},
crossDomain = false,
global = true,
// 在这里设置默认值
} = {}) {
// 这里则是赋值的内容,若为undefined,则使用默认值
};
复制代码
遍历Map结构 上文说过,面对拥有Iterator
接口的对象时,可使用解构赋值。在这里,咱们能够经过解构赋值快速的获取键名和键值。
const map = new Map();
map.set('first', 'hello');
map.set('second', 'world');
for (let [key, value] of map) {
console.log(key + " is " + value);}
// first is hello
// second is world
复制代码
输入模块的指定方法 加载模块时,须要指定输入哪些方法。解构赋值使得输入语句很是清晰。
const { SourceMapConsumer, SourceNode } = require("source-map");
复制代码
在使用解构赋值的时候,总体感受上其实就是一个遍历过程的简化。我的感受最大的做用是能够将相似逻辑的代码进行过程简化,从而给代码瘦身。
同时在其中也发现了原文章中的部分细节错误。例如不能使用圆括号的状况中的第一点,示例代码中的前两行代码并无报错,而是显示undefined
。
而后这里给本身留一个小做业,是在和朋友聊上述细节错误时发现的一个问题:为何let [(a)] = [1];
显示undefined
,而用[(a)] = [1]
则会显示[1]
?