变量解构赋值(一)数组解构赋值1、基本用法ES6允许按照一定的模式从数组和对象中提取值,并赋值给变量,这叫做解构(Destructuring)。只要等号两边的模式相同,左边的变量就会被赋予相应的值。让[foo,[[bar],baz]]=[1,[[2],3]];foo//1foo//1bar//2baz//3let[,,third]=["foo","bar","baz"];third//"baz"let[x,,y]=[1,2,3];x//1y//3let[head,...tail]=[1,2,3,4];head//1tail//[2,3,4]let[x,y,...z]=['a'];x//"a"y//undefinedz//[]如果解构不成功,则变量的值等于undefined。var[foo]=[];var[bar,foo]=[1];以上两种情况属于解构失败,foo的值会等于undefined。如果等号右边不是数组,会报错。//错误报告let[foo]=1;let[foo]=false;let[foo]=NaN;let[foo]=undefined;let[foo]=null;let[foo]={};解构赋值不仅适用于var命令,也适用于let和const命令。var[v1,v2,...,vN]=数组;让[v1,v2,...,vN]=数组;const[v1,v2,...,vN]=数组;对于Set结构,也可以使用数组的解构赋值。让[x,y,z]=newSet(["a","b","c"]);x//"a",只要某个数据结构有Iterator接口,就可以解构并以数组的形式赋值。function*fibs(){vara=0;varb=1;while(true){yielda;[a,b]=[b,a+b];}}var[第一,第二,第三,第四,fifth,sixth]=fibs();sixth//5在上面的代码中,fibs是一个带有Iterator接口的Generator函数。解构赋值将依次从该接口获取值。2.默认值解构赋值允许指定默认值。var[foo=true]=[];foo//真[x,y='b']=['a'];//x='a',y='b'[x,y='b'']=['a',undefined];//x='a',y='b'请注意,ES6在内部使用严格的相等运算符(===)来确定某个位置是否有值。因此,如果一个数组成员不严格等于undefined,默认值将不会生效。var[x=1]=[未定义];x//1变量[x=1]=[null];x//null上面代码中,如果一个数组成员为null,则默认值不会生效,因为null并不严格等于undefined。如果默认值是一个表达式,那么这个表达式是惰性求值的,也就是只有在被使用的时候才会求值。函数f(){console.log('aaa');}让[x=f()]=[1];上面的代码中,因为x可以取到值,所以根本不会执行函数f。上面的代码实际上等同于下面的代码。letx;if([1][0]===undefined){x=f();}else{x=[1][0];}默认值可以引用解构赋值的其他变量,但是变量必须已经声明。让[x=1,y=x]=[];//x=1;y=1让[x=1,y=x]=[2];//x=2;y=2让[x=1,y=x]=[1,2];//x=1;y=2让[x=y,y=1]=[];//上面ReferenceError最后一个表达式之所以报错,是因为x在使用默认值y的时候,还没有声明y。(2)对象解构赋值解构不仅可以用于数组,也可以用于对象。var{foo,bar}={foo:"aaa",bar:"bbb"};foo//"aaa"bar//"bbb"对象的解构在一个重要方面不同于数组:数组的元素是有序排列的,变量的值是由它的位置决定的;虽然对象的属性没有顺序,但变量必须与属性同名才能获得正确的值。var{bar,foo}={foo:"aaa",bar:"bbb"};foo//"aaa"bar//"bbb"var{baz}={foo:"aaa",bar:"bbb"};baz//undefined上面代码的第一个例子中,等号左边的两个变量的顺序和等号右边的两个同名属性的顺序不一致,但是它对价值根本没有影响。第二个例子中的变量没有对应的同名属性,所以取不到值,最后等于undefined。如果变量名和属性名不一致,必须这样写。var{foo:baz}={foo:'aaa',bar:'bbb'};baz//"aaa"letobj={first:'hello',last:'world'};let{first:f,last:l}=obj;f//'hello'l//'world'对象解构赋值的内部机制是先找到同名属性,然后赋值给对应的变量。实际分配的是后者,而不是前者。var{foo:baz}={foo:"aaa",bar:"bbb"};baz//"aaa"foo//error:fooisnotdefined在上面的代码中,实际赋值的是变量baz,而不是schema富。注意,使用这种写法时,变量的声明和赋值是集成在一起的。对于let和const来说,变量是不能被重新声明的,所以一旦被赋值的变量之前声明过,就会报错。让foo;让{foo}={foo:1};//SyntaxError:重复声明"foo"letbaz;let{bar:baz}={bar:1};//SyntaxError:Duplicatedeclaration"baz"ifnone对于第二个let命令,上面的代码不会报错。让foo;({foo}={foo:1});//成功让baz;({bar:baz}={bar:1});//success上面的代码中,let命令下面这一行的括号是必须的,否则会报错。因为解析器会将左花括号理解为代码块,而不是赋值语句。与数组一样,解构也可用于具有嵌套结构的对象。varnode={loc:{start:{line:1,column:5}}};var{loc:{start:{line}}}=node;line//1loc//错误:loc未定义开始//错误:startisundefined上面代码中只有line是一个变量,loc和start都是pattern,不会被赋值。对象解构也可以指定默认值。var{x=3}={};x//3var{x,y=5}={x:1};x//1y//5var{x:y=3}={};y//3var{x:y=3}={x:5};y//5var{message:msg='Somethingwentwrong'}={};msg//默认值“Somethingwentwrong”的条件效果是对象的属性值严格等于undefined。var{x=3}={x:undefined};x//3var{x=3}={x:null};x//null上面代码中,如果x属性等于null,则不严格等于undefined,默认值不会生效。如果解构失败,则变量的值等于未定义。如果解构方式是嵌套对象,子对象所在的父属性不存在,就会报错。//报错var{foo:{bar}}={baz:'baz'};上面代码中,等号左边的对象的foo属性对应的是一个子对象。子对象的bar属性在解构时会报错。原因很简单,因为此时foo等于undefined,如果再取子属性会报错。//写法错误varx;{x}={x:1};//SyntaxError:syntaxerror上面的代码会报错,AsyntaxerroroccursbecausetheJavaScriptengineinterprets{x}asacodeblock.这个问题只能通过不在行首写花括号来解决,这样JavaScript就不会把它解释为代码块。//正确的写法({x}={x:1});对象的解构赋值可以很方便的将已有对象的方法赋值给一个变量。让{log,sin,cos}=数学;由于数组本质上是一个特殊的对象,所以可以在数组上解构对象属性。vararr=[1,2,3];var{0:first,[arr.length-1]:last}=arr;first//1last//3上面的代码在数组上执行对象结构。数组arr的0键对应的值为1,[arr.length-1]为2键,对应的值为3。(3)字符串的解构赋值字符串也可以解构赋值。这是因为此时,字符串被转换为类数组对象。const[a,b,c,d,e]='你好';a//"h"b//"e"c//"l"d//"l"e//"o"Array-likeObjects有一个length属性,所以这个属性也可以被解构和赋值。let{length:len}='hello';len//5(4)解构赋值数值和布尔值解构赋值时,如果等号右边是数值或布尔值,则首先转换为对象。let{toString:s}=123;s===Number.prototype.toString//truelet{toString:s}=true;s===Boolean.prototype.toString//true在上面的代码中,值和布尔值value包装对象具有toString属性,因此变量s可以获得值。解构赋值的规则是只要等号右边的值不是对象,就先转为对象。由于undefined和null不能转化为对象,解构赋值会报错。让{prop:x}=undefined;//TypeErrorlet{prop:y}=null;//TypeError(5)解构赋值函数参数也可以使用解构赋值。函数添加([x,y]){返回x+y;}添加([1,2]);//3个函数参数的解构也可以使用默认值。函数移动({x=0,y=0}={}){返回[x,y];}移动({x:3,y:8});//[3,8]移动({x:3});//[3,0]移动({});//[0,0]移动();//[0,0]上面代码中,函数move的参数是一个对象,通过this对象解构得到变量x和y的值。如果解构失败,则x和y等于默认值。undefined将触发函数参数的默认值。[1,undefined,3].map((x='yes')=>x);//[1,'yes',3](6)括号问题虽然解构赋值很方便,但是不容易实现解析容易。对于编译器来说,从一开始就无法知道公式是模式还是表达式,必须解析(或不解析)才能知道等号。由此产生的问题是,如果模式中有括号,该怎么办。ES6规则是,只要存在可能导致解构的歧义,就不得使用括号。一、不能使用圆括号的情况(1)变量声明语句中不能使用圆括号。//所有错误var[(a)]=[1];var{x:(c)}={};var({x:c})={};var{(x:c)}={};var{(x):c}={};var{o:({p:p})}={o:{p:2}};(2)函数参数中,pattern不能有括号。//错误函数f([(z)]){returnz;}(3)在赋值语句中,整个模式,或一层嵌套的模式,不能放在括号中。//所有错误({p:a})={p:42};([a])=[5];上面的代码将整个模式放在括号中,导致错误。2.可以使用圆括号的场合只有一种情况可以使用圆括号:赋值语句的非模态部分。[(b)]=[3];//正确({p:(d)}={});//正确[(parseInt.prop)]=[3];//正确,上面三行都OK正确执行了,因为首先,都是赋值语句,不是声明语句;其次,它们的括号都不是模式的一部分。语句的第一行,mode是获取数组的第一个成员,与括号无关;在语句的第二行中,模式是p而不是d;第三行语句与第一行语句的性质一致。(7)、利用(1)交换变量的值[x,y]=[y,x];交换变量x和y的值(2)从函数返回多个值//返回一个数组函数example(){return[1,2,3];}var[a,b,c]=example();//返回一个对象functionexample(){return{foo:1,bar:2};}var{foo,bar}=example();(3)函数参数的定义//参数是一组有序值functionf([x,y,z]){...}f([1,2,3]);//参数是一组无序值functionf({x,y,z}){...}f({z:3,y:2,x:1});(4)提取JSON数据varjsonData={id:42,status:"OK",data:[867,5309]};let{id,status,data:number}=jsonData;console.log(id,status,number);//42,"OK",[867,5309](5)函数参数的默认值jQuery.ajax=function(url,{async=true,beforeSend=function(){},cache=true,complete=function(){},crossDomain=false,global=true,//...更多配置}){//...做东西};指定参数的默认值,避免写varfoo=config.foo||'默认在函数体内foo';这样的说法。(6)遍历Map结构任何用Iterator接口部署的对象都可以用for...of循环遍历。Map结构原生支持Iterator接口,通过变量解构赋值获取键名和键值非常方便。varmap=newMap();map.set('first','hello');map.set('second','world');for(let[key,value]ofmap){console.log(key+"is"+value);}//firstishello//secondisworld//getkeynamefor(let[key]ofmap){//...}//获取键值(let[,value]ofmap){//...}(7)输入模块的指定方法const{SourceMapConsumer,SourceNode}=require("source-map");持续更新中~~~喜欢的话请留言辛苦了!
