您的位置:首页 > 其它

变量的解构赋值 ES6

2017-04-18 18:49 656 查看
1.先来说说数组的解构赋值。

【基本用法】

ES6允许按照一定模式,从数组和对象中提取值,然后对变量进行赋值,这种被称为解构。

  var a = 1;

  var b = 2;
  var c = 3;

//ES6:

var  [a,b,c] = [1,2,3];//本质上,这种称为模式匹配,只要等号两边的模式相同,左边变量就会被赋予右边对应的值

下面是嵌套数组进行解构的例子

let [foo,[[bar],baz]] = [1,[[2],3]];

foo //1

bar //2

baz //3

let [ , , third] = ["foo","bar","baz"];

third //"baz"

let [x, , y] = [1,2,3];

x // 1;

y // 3;

let [head,...tail] = [1,2,3,4];

head // 1

tail //[2,3,4]

let [x,y,...z] = ['a'];

x // "a"

y // undefined

z // []

(1)如果解构不成功,变量的值就等于undefined

  var [foo] = [];

  var [bar,foo] = [1];

  //以上两种情况都属于解构不成功,foo的值都会等于undefined

(2)另一种情况,就是不完全解构,即等号左边的模式只匹配等号右边数组的一部分,这种情况下,解构依然可以成功。

let [x,y] = [1,2,3];

x // 1

y // 2

let [a,[b],d] = [1,[2,3],4];

a // 1

b // 2

d // 4

上面的两个例子都属于不完全解构,但是可以成功

(3)如果等号右边不是数组,不是可以遍历的解构,那么就会报错。

解构赋值不仅适用于var命令,也适用于let和const命令

  var [v1,v2,...,vN] = array;

  let  [v1,v2,...,vN] = array;

  const [v1,v2,...,vN] = array;

对于Set结构,也可以使用数组的解构赋值

let [x,y,z] = new Set(["a","b","c"])

x // "a"

事实上,只要某种数据结构具有Iterator接口,都可以采用数组形式的解构赋值。

  function* fibs(){

    var a = 0;

    var b = 1;

    while(true){

      yield a;

      [a,b] = [b,a+b];

    }

  }

  var [first,second,third,fourth,fifth,sixth] = fibs();

  console.log(first); // 0

  console.log(second); // 1

  console.log(third); // 1

  console.log(fourth); // 2

  console.log(fifth); // 3

  console.log(sixth); // 5

在看书上这部分的时候,不是太懂,然后通过打印每个值,发现了一丢丢原理。不过这只是我的一点理解,也不知道对不对。

上面这个fibs这个函数,里面一直在对[a,b]这个数组赋值,在进入while循环的时候,[a,b]就已经有一个值了:[0,1].然后随着while循环的继续执行,

[a,b]这个数组就一直被赋值:

[a,b] = [0,1]

[a,b] = [1,1];

[a,b] = [1,2];

[a,b] = [2,3];

[a,b] = [3,5];

[a,b] = [5,8];

然后var [first,second,third,fourth,fifith,sixth] = fibs();

解构赋值会依次从这个接口获取值。yield a,这里简单粗暴的理解为返回a的值,所以打印出来的first到sixth是0 1 1 2 3 5.

【默认值】

解构赋值允许指定默认值

  var [foo = true] = [];

  foo // true

  [x,y = 'b'] = ['a'] //x= 'a',y = 'b'

  [x,y = 'b'] = ['a',undefined] // x = 'a',y= 'b'

注意:ES6内部使用严格相等运算符(===)判断一个位置是否有值,所以,如果一个数组成员不严格等于undefined,默认值是不会生效的

  var [x=1] = [undefined];

// x = 1

  var [x =1] = [null];

  x//null  如果一个数组成员时null,默认值就不会生效,

如果默认值是一个表达式,那么这个表达式是惰性求值的,即只有在用到的时候才会求值。

  function f(){

    console.log('aaa');

  }

  let [x = f()] = [1];//这时候x能取到值,所以f函数根本不会执行。

默认值可以引用解构赋值的其他变量,但该变量必须已经声明

  let  [x = 1,y = x] = [];//x = 1,y = 1

  let [x =1,y = x] = [2];//x = 2,y = 2

  let [x = 1,y = x] = [1,2];//x = 1,y = 2

  let [x = y,y = 1] = [];//报错

  因为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

如果变量名与属性名不一致,可以写成下面这样:

  var {foo:baz} = {foo:"aaa",bar:"bbb"};

  baz // "aaa"

  let obj = {first:"aaa",last:"bbb"};

  let{first:f,last:l} = obj

  f  //"aaa"

  l //"bbb"

  这个实际上说明,对象的解构赋值是以下形式的简写:

  var  {foo:foo,bar:bar} = {foo:"aaa",bar:"bbb"};

也就是说,对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,不是前者。

采用这种写法时,变量的声明和赋值是一体的,对于let和const而言,变量不能重新声明,所以一旦赋值的变量以前声明过,就会报错。

let foo;

let {foo} = {foo:1};//报错,已经声明过了

let baz;

let {bar:baz} = {bar:1};//baz已经声明过了

注意:不过这个错误只会在使用let和const命令时出现,因为var允许重新声明

  let foo;

  ({foo}={foo:1});//成功

和数组一样,解构也可以用于嵌套解构的对象

  var obj = {p:["hello",{y:"world"}]};

  var {p:[x,{y}]} = obj;

  x//"hello"

  y//"world"

注意这时候p是模式,不是变量,所以不会被赋值

  var node = {

    loc:{

      start:{

        line:1,column:5

      }

    }

  }

  var {loc:{start:{line:1,column:5}}} = node;

  line //1

  loc //loc is undefined

  start // start is undefined

  上面代码中,只有line是变量,loc和start都是模式,不会被赋值

  下面是嵌套赋值的例子

  ({foo:obj.prop,bar:arr[0]} = {foo:123,bar:true});

  obj//{prop:123}

  arr //[true]

  对象的解构赋值也可以指定默认值

  var {x = 3} ={};

  x //3

  var {x,y = 5} = {x:1};

  x // 1

  y // 5

  var {message :msg = "something went wrong"} = {};

  msg // something went wrong

  默认值生效的条件是,对象的属性值严格等于undefined

  var {x = 3} = {x :undefined};

  x // 3

  var {x=3} = {x:null};

  x // null

  如果解构失败,变量的值等于undefined

  var {foo} ={bar:'baz'};

  foo // undefined

  如果解构模式是嵌套的对象,而且子对象所在的父属性不存在,会报错。

  var {foo:{bar}} = {baz:'baz'};

  foo这是等于undefined,再取子属性就会报错

  

  //错误的写法:

  var x;

  {x} = {x:1};//这时候javascript引擎会把{x}理解成一个代码块,从而发生错误。

  //正确的写法

  ({x} = {x:1});

  还有解构赋值,允许等号左边的模式中不放置任何变量名:

  ({} = [true,false]);

  ({} = 'abc');

  ({} = [])

  //这些表达式虽然没有意义,但是语法是合法的,可以执行

  对象的解构赋值可以很方便的将现有对象的方法赋值到某个变量

  let {log,sin,cos} = Math;

  //上面将Math对象的取对数,正弦,余弦三个方法赋值到了对应的变量上,这样使用起来会方便很多

3.字符串的解构赋值

字符串也可以解构赋值。这是因为字符串被转换成了一个类似数组的对象。

  const [a,b,c,d,e] = 'hello';

  a // 'h' b// 'e' ...

  //类似数组的对象都有length属性,因此还可以对这个属性解构赋值。

  let {length:len} = 'hello'

  len // 5

```

4.数值和布尔值的解构赋值

在解构赋值时,如果等号右边是数值或者布尔值,则先转换为对象

let {toString:s} = 123;

s === Number.prototype.toString // true

let {toString:s} = true;

s === Boolean.prototype.toString // true

上面的代码中,数值和布尔值的包装对象都有toString属性,因此变量s都能取得值

解构赋值的规则是:只要等号右边的值不是对象,就先将其转为对象,由于undefined和null无法转化为对象,所以对他们进行解构赋值都会报错。

let {prop:x} = undefined;//TypeError

let {prop:y} = null;//TypeError

```

5.函数参数的解构赋值

  function add([x,y]){

    return x+y;

  }

  add([1,2]) // 3 注意add的参数不是一个数组,而是通过解构得到的变量x,y

其次,函数参数的解构赋值,也可以是默认值,得看是赋值给等号左边,还是等号右边,等号左边是变量,等号右边是参数。

给定一个例子来理解

(1)默认值在等号左边

  function move({x=0,y=0} = {}){

    return [x,y];

  }

  move({x:3,y:8}); // [3,8]

  move({x:3}); // [3,0]

  move({});//[0,0]

  move();//[0,0]

  上面的代码中,函数move的参数是一个对象,通过对这个对象进行解构,得到变量x和y的值。

  如果解构失败,则x和y等于默认值

(2)默认值在等号右边

  function move({x,y} = {x:0,y:0}){

    return [x,y];

  }

  move({x:3,y:8});// [3,8]

  move({x:3});// [3,undefined]

  move({}) // [undefined,undefined]

  move() // [0,0]

上面代码是为函数move的参数指定默认值,而不是为变量x和y指定默认值,所以会得到与前一种不同的结果。

这就是默认值在等号左边和等号右边的区别。一个是真正为变量赋值(左边),一个是为函数参数赋值(右边)。

还有一点:undefined可以触发函数参数的默认值

[1,undefined,3].map((x = 'yes') =>x)

```

6.圆括号问题

ES6的规则是,只要可能导致解构歧义,就不得使用圆括号。

因此,建议只要有可能,就不要在模式中放置圆括号

(1)不能使用圆括号的情况

1.变量声明语句中,模式不能带有圆括号

var [(a)] = [1];

var {x:(c)} = {};

var {o:({p:p})} = {o:{p:2}};

上面三条语句都会报错,因为它们都是变量声明语句,模式不能使用圆括号。

2.函数参数中,模式不能带有圆括号

函数参数也属于变量声明,因此不能带有圆括号

//报错

function f([(z)]) {return z;}

3.不能将整个模式或嵌套模式中的一层放在圆括号中、

//报错

({p:a}) = {p:42};

([a]) = [5];

(2)可以使用圆括号的情况

只有一种:赋值语句的非模式部分可以使用圆括号

[(b)] =[3]; // 正确

({p:(d)} = {});//正确

[(parseInt.prop)] = [3];//正确

上面三条都是正确的,首先他们都是赋值语句,而不是声明语句,其次,它们的圆括号都不属于模式的一部分。

7.用途

(1)变换变量的值

[x,y] = [y,x];

上面代码交换了变量x和y的值

(2)从函数返回多个值

函数只能返回一个值,如果要返回多个值,只能将其放在数组或对象中返回。有了解构赋值,取出这些值就非常方便

//返回一个数组

  function example(){

    return [1,2,3];

  }

  var [a,b,c] =example();

//返回一个对象

function example(){

  return {

    foo:1,

    baz:2

  };

}

var {foo,bar} = example();

(3)函数参数的定义

解构赋值可以方便的将一组参数与变量名对应起来

参数是一组有次序的值(数组)

  function f([x,y,z]){

    ...

  }

  f([1,2,3]);

//参数是一组无次序的值(对象)

  function f([x,y,z]){...}

  f({z:3,y:2,x:1});

(4)提取JSON数据

解构赋值对提取JSON对象中的数据尤其有用

var jsonData = {

  id:42,

  status:"ok",

  data:[867,5309]

}

let {id,status,data:number} = jsonData;

console.log(id,status,number);

//42,ok,[867,5309]

上面的代码可以快速提取JSON数据的值

(5)函数参数的默认值

  jquery.ajax = function(url,{
    async = true,

    beforeSend = function(){},

    cache = true,

    // .. more config

  }){

    //... do stuff

  };

//指定参数的默认值,就避免了在函数体内部再写var foo = config.foo

(6)遍历Map结构

任何部署了Iterator接口的对象,都可以用for ... of循环遍历。Map结构原生支持Iterator接口,配合变量的解构赋值,获取键名和键值就非常方便

  var 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

如果只想获得键名,或者只想获取键值,可以写成下面这样

  //获取键名

  for(let [key] of map){

    //...

  }

  //获取键值

  for(let [,value] of map){

    //...

  }

(7)输入模块的指定方法

  加载模块时,往往需要指定输入哪些方法。解构赋值使得输入语句非常清晰

  const {SourceMapConsumer,SourceNode} = require("source-map");
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: