ES6系列入门学习记录:变量的解构赋值
栏目: JavaScript · 发布时间: 5年前
内容简介:年都过去半个月了,我终于又重新开始更新了。虽然只是第二篇,但是我会继续加油努力,一定不会放弃更新的。在文章中若有什么不妥或者您有更多建议的话,欢迎和期待您给我留言,您的每一个留言都可能成为我进步的助力,十分感谢。那就废话不多说直接开始吧。解构赋值,顾名思义,就是解开一个结构,然后从里面拿出值用来给变量赋值赋值。所以解构赋值主要是以数据类型来划分的。上述代码算是最简单的数组解构赋值,其实也可以看做是数据的另一种展示。比如上述代码与下面的代码其实是一样的。
年都过去半个月了,我终于又重新开始更新了。虽然只是第二篇,但是我会继续加油努力,一定不会放弃更新的。在文章中若有什么不妥或者您有更多建议的话,欢迎和期待您给我留言,您的每一个留言都可能成为我进步的助力,十分感谢。那就废话不多说直接开始吧。
概念
解构赋值,顾名思义,就是解开一个结构,然后从里面拿出值用来给变量赋值赋值。所以解构赋值主要是以数据类型来划分的。
数组的解构赋值
var [a,b,c] = [1,2,3]; 复制代码
上述代码算是最简单的数组解构赋值,其实也可以看做是数据的另一种展示。比如上述代码与下面的代码其实是一样的。
var a=1,b=2,c=3; 复制代码
所以解构赋值最主要的作用,是可以让我们简化提取值的过程。
本质上,这种写法属于“模式匹配”,同模式下,左边的变量就会被赋予对应位置的右边的值。例如:
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
。
当匹配两边的模式相同,且长度不同时,此时的解构赋值被称为不完全解构。虽然叫不完全解构,但是依旧算解构成功的。
let [a,b] = [1,2,3]; a //1 b //2 复制代码
上面一直提到一个前提情况,那就是模式相同,没错,这是比较需要注意的一点。当两边模式不同时,解构赋值是会报错的。
let [a] = 1; let [b] = false; let [c] = {}; 复制代码
在不严谨的情况下,我们可以说,当两边的数据类型不同时,解构赋值会出现报错。
只要某种数据结构具有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]
?
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
高分辨率遥感卫星应用
张永 / 科学分社 / 2004-1 / 48.00元
高分辨率遥感卫星应用(成像模型处理算法及应用技术),ISBN:9787030128249,作者:张永生等著一起来看看 《高分辨率遥感卫星应用》 这本书的介绍吧!