解构: ES6 中允许按照一定的模式从数组和对象中提取值,然后对变量进行赋值,这被称为解构(Destructuring).
本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予相应的值。
完全解构let [a, b, c] = [1, 2, 3];
console.log(a); // 输出1
let [a, b] = [1];
console.log(b); // 解构不成功时返回undefined
不完全解构
等号左边只匹配到等号右边的一部分。
let [a, [b], c] = [1, [2, 3], 4];
console.log(b); // 输出2,只匹配到右边的右边数组[2, 3]中的2
不能解构情况
如果等号右边不是可遍历的解构,或者说等号右边的值或是转换为对象以后也不具备 Iterator 接口,那么就会解构失败。
let [a] = 1;
let [a] = false;
let [a] = NaN;
let [a] = undefined;
let [a] = null;
let [a] = {}
总结:事实上,只要某种数据结构具有 Iterator 接口,都可以采用数组形式的解构赋值。
解构赋值允许指定默认值。
ES6内部使用严格相等运算符(===)判断数组的某个位置是否有值。所以,如果一个数组成员不严格等于undefined,默认值是不会生效的。
let [a = 2] = [null];
// a = null
let [a = 2] = [];
// a = 2
如果默认值是一个表达式,那么这个表达式是惰性求值的
function f() {
return 2;
}
let [a = f()] = [1];
// a = 1, f函数不会执行
let [a = f()] = [];
// a = 2, f函数会执行
默认值可以引用解构赋值的其它变量,但是该变量必须已经声明
let [x = 1, y = x] = [];
// x = 1, y = 1
let [x = y, y = 1] = [];
// ReferenceError,在使用变量y时还并没有声明
对象解构赋值的内部机制是先找到同名属性,然后再赋值给对应的变量,真正被赋值的是后者而不是前者
举例说明:
let {foo:bar} = {foo:"aaa"};
console.log(bar); // bar = "aaa"
console.log(foo); // ReferenceError, foo is not defined
console.log({foo:bar}.foo); // {foo:bar}.foo = "aaa"
上面代码中 foo 是匹配的模式,通过 foo 匹配到对象 {foo:bar} 中 foo 属性的值( bar 变量),然后将值赋给 bar 变量,这样对象 {foo:bar} 中 foo 属性就有值了,即 bar 的值 "aaa".
// 实际上
let {foo1, foo2} = {foo1:"aaa", foo2:"bbb"};
// 是下面语句的简写形式
let {foo1:a, foo2:b} = {foo1:"aaa", foo2:"bbb"};
对象的解构也可以指定默认值,默认值生效的条件是,对象的属性值严格等于undefined
let {x = 3} = {x: undefined};
// x = 3
// 上面的语句等价于
let {x:x = 3} = {x: undefined};
// x = 3
// {x:x = 3} = 3
let {x = 3} = {x: null};
// x = null
如果解构模式是嵌套的对象,而且子对象所在的父属性不存在,那么将会报错
let {foo: {bar}} = {bar: 'bar'};
// 报错,因为foo = {bar} = undefined,{bar}对象中的bar属性在解构时会报错,因为{bar}是undefined,undefined是不能转换为对象的,对undefined取bar属性会报错。
// 和下面的代码原理一样:
let obj = {bar: "bar"};
obj.foo.bar // 报错,因为obj.foo = undefined,对undefined取属性会报错。
"bar".foo // undefined,不会报错,因为字符串可以转换为对象
在将已经声明的变量进行解构赋值时,要注意解构赋值语句不能直接写在行首
let x;
{x} = {x: 1}; // 报错,这个地方不是很懂
// 书上的解释是:JS引擎将{x}理解成一个代码块,从而发生语法错误。
// 若想避免这个错误,可以这样写:
({x} = {x: 1});
字符串可以解构赋值是因为字符串可以转换成一个类似数组的包装对象。
let [a, b, c, d, e] = "hello";
// a = 'h' b = 'e' c = 'l' d = 'l' e = 'o'
由于类似数组的包装对象有一个 length 属性,因此在解构赋值时可以利用这个属性。
let {length: len} = 'hello';
// len = 5
解构赋值时,如果等号右边是数值或布尔值,则会先转为对象
let {toString: s} = 123;
console.log(s === Number.prototype.toString) // true
数值 123 被转换为对象,数值对象中有 toString 方法,与 toString 匹配,变量 s 中存储的是 toString 方法,该方法就是 Number 原型对象中的 toString 方法。
函数参数的解构也可以使用默认值
function f({x = 0, y = 0} = {}) {
return {x, y};
}
console.log(f({x: undefined, y: 1}));
// 输出: {0, 1}
详细过程:调用函数f后,{x = 0, y = 0}={x:undefined,y:1}
由于x是undefined,故x使用默认值即x=0,由于y:1故通过解构赋值后y=1.
之前的误区:注意是解构赋值,并不是对象之间的赋值。
再看下面这个例子:
function f({x, y} = {x: 0, y: 0}) {
return {x, y};
}
console.log(f({x: undefined, y: 1}));
// 输出: {undefined, 1}
// 调用函数后,{x,y}={x:udefined,y:1},解构赋值后x=undefined,y=1
console.log(f({x: 1, y: 1}));
// 输出: {1, 1}
// 调用函数后,{x,y}={x:1,y:1},解构赋值后x=1,y=1
console.log(f({}));
// 输出: {undefined, undefined}
// 调用函数后,{x,y}={}={undefined,undefined},解构赋值后x=undefined,y=undefined
console.log(f());
// 输出: {0, 0} 当不传递实参时,形参的值是原有的值
// 调用函数后,{x,y}={x:0,y:0},解构赋值后x=0,y=0
再次重申一下 {x,y}={x:1,y:2} 这种形式是解构赋值,千万不要理解成对象之间的赋值!
对于编译器而言,一个式子到底是模式还是表达式,没有办法一开始就知道,必须解析到(或解析不到)等号才能知道
不能使用圆括号的情况
变量声明语句函数参数赋值语句表达式可以使用圆括号的情况
赋值语句中的非模式部分可以使用圆括号。
[(a)] = [3]; // a并不是模式
({ p: (d) } = {}); // p是模式,d并不是模式
建议:无论什么情况都尽量不要在模式中使用圆括号
交换变量的值
let a = 1;
let b = 2;
[a, b] = [b, a];
方便处理函数返回值
function f([x, y]) {
return [x+1, y+2];
}
let [a, b] = f([1, 1]);
函数参数定义
解构赋值可以方便地将一组参数与变量名对应起来。
function f1({x, y, z}){
return x+y+z;
}
f1({y: 1, z: 2, x: 3 }); // 可以做到实参没有次序
提取JSON数据
可以很方便地从JSON中提取需要的数据。
let JSON = {
name: "happyCoding1024",
age: 18,
hobby: "coding"
}
let [name, age, hobby] = JSON;
函数参数默认值
非常简化方便地使用函数参数默认值。
function f([x=0,y=0] = []){
return x+y;
}
f([]); // 当传入的是undefined时,就会使用默认值
遍历Map结构
任何部署了 Iterator 接口的对象都可以用 for...of 循环遍历。Map 结构原生支持 Iterator 接口,配合变量的解构赋值获取键名和键值非常方便。
let map = new Map();
map.set('first', 'hello');
map.set('second', 'world');
for (let [key, value] = map){
console.log(key + "is" + value);
}
输入模块的指定方法
加载模块时,往往需要制定输入的方法,解构赋值使得输入语句非常清晰。
const {SourceMapConsumer, SourceNode} = require("source-map");
解构可以用在函数传递参数的过程中。我先先来看一下没有用到解构参数的例子,之后我们把它改造成解构参数。在这个例子里面我们使用解构参数改写了函数声明
交换变量:通常交换两个变量的方法需要一个额外的临时变量,来看看例子;访问数组中元素有种场景,咱们可能有一个为空的项数组。并且希望访问数组的第一个、第二个或第n个项
解构的同时赋予初始值(使用 =),2.使用的变量名与解构目标的key不一致或要使用多个变量(使用 : ),解构中的 rest(变量由多变少) 与spread(变量由少变多)
解构:从数组和对象中提取值,对变量进行赋值。数组的解构赋值1.数组的元素是按次序排列的,变量的取值由它的位置决定;对象的解构赋值1.对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
解构的作用:简化书写长度,提升开发效率。在开发中我们常用到使用ajax请求数据,并且把数据渲染到页面上。可能这个数据返回的对象或数组。
ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构。它在语法上比ES5所提供的更加简洁、紧凑、清晰。它不仅能减少你的代码量,还能从根本上改变你的编码方式。
temp是一个临时变量,它先保存a的值。然后把b的值赋值给a,接着将temp值赋给 b。如果使用解构的方式会更简单,不需要什么鬼的 temp 变量。
解构可以用于数组,还可以用于对象。对象的解构赋值,可以很方便地将现有对象的方法,赋值到某个变量。
es6新增了一种从数组或者对象中获取指定元素的方式,这种方式就是我们今天要说的解构。在有解构之前呢,我们获取数组中的指定元素通常是根据索引去做的:
相比于传统方法需要一个额外变量来进行值交换,使用解构进行值交换十分方便。但是我想到几个问题:解构赋值有没有节省空间呢?
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!