ES6学习笔记2:变量的解构赋值

目录

解构

定义

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

数组的解构赋值

基本用法

  • 之前赋值用法,只能直接指定值
let a = 1;
let b = 2;
let c = 3;
  • 在ES6中可以这样写:
let [a, b, c] = [1, 2, 3];

上面的代码可以从数组中提取值,按照对应的位置,对变量赋值。本质上,这种写法属于“模式匹配”,只要等号两边的模式一样,左边的变量就会被赋予对应的值。

嵌套使用
// 嵌套数组
let [foo, [[bar], baz]] = [1, [[2], 3]];
console.log(foo); // 1
console.log(bar); // 2
console.log(baz); // 3

let [,,third] = [1,2,3];
console.log(third); // 3

let [x, , y] = [1,2,3];
console.log(x); // 1
console.log(y); // 2

let [head, ...tail] = [1,2,3,4];
console.log(head); // 1
console.log(tail); // [2,3,4]

let [x,y,...z] = ['a'];
console.log(x); // a
console.log(y); // undefined
console.log(z); // []

  • 如果解构不成功,那么变量的值就是undefind.
不完全解构

当等号左边只能匹配等号右边的一部分的数组,这种情况下依旧可以成功解构。

let [x,y] = [1,2,3]
console.log(x); // 1
console.log(y); // 2

let [a, [b], d] = [1, [2,3],4];
console.log(a); // 1
console.log(b); // 2
console.log(d); // 4

上面都属于不完全解构,但是都可以解构成功。

  • 如果等号右边不是数组(或者是不可以遍历的结构),那么将会报错。
let [foo] = 1;  //Type '1' is not an array type.
let [foo] = false;
let [foo] = NaN;
let [foo] = undefined;
let [foo] = null;
let [foo] = {}; // Type '{}' is not an array type.

上面都会报错。

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

设置默认值

解构赋值允许指定默认值

let [foo = true] = [];
console.log(foo); // true

let [x, y='b'] = ['a'];
console.log(x); // 'a'
console.log(y); // 'b'

let [x, y='b'] = ['a', undefined];
console.log(x); // 'a'
console.log(y); // 'b'

在ES6内部使用严格相等(===)来判断一个值是否有效。所以在设置某一个值严格为undefined时,默认值才会生效。

let [x = 1] = [undefined];
console.log(x); // 1

let [x = 1] = [null];
console.log(x); // null
  • 如果默认值是一个表达式,那个这个表达式是惰性求值的,只用在用到的时候才会求值。
function f() {
      console.log('aaa');
}

let [x = f()] = [1];
console.log(x); // 1

因为这里的x是可以取到值为1的,所以这个的f()是不会执行的。

function f() {
      console.log('aaa');
}

let [x = f()] = [];
console.log(x); // undefined

因为这里的x已经取不到值了,所以这里的f()会执行,但是由于f()没有任何的返回值,所以这里会在控制台上输出aaa,但是x的值为undefined.

在下面这种情况时,x是可以取到值的:

function f() {
     return 3
}

let [x = f()] = [];
console.log(x); // 3
  • 默认值可以引用解构赋值的其他变量,但是在引用前,要引用的变量已经声明。
let [x = 1, y = x] = [] ;
console.log(x); // 1
console.log(y); // 1

let [x = 1, y = x] = [2] ;
console.log(x); // 2
console.log(y); // 2

let [x = 1, y = x] = [1,2] ;
console.log(x); // 1
console.log(y); // 2

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

最后一个报错是因为,x在引用y的时候,y还没有声明。

对象的解构赋值

对象的解构赋值和数组解构赋值的区别

  • 数组解构赋值的元素是按次序排列的,变量的取值由它的位置决定
  • 对象解构赋值的属性没有次序,变量必须和属性同名,才能取到正确的值

基本用法

let { bar, foo}={foo:'aaa',bar:'bbb'};
console.log(bar); // 'bbb';
console.log(foo); // 'aaa';

let { baz } = { foo: 'aaa', bar: 'bbb' };
console.log(baz); // undefined
  • 如果解构失败,那么变量的值就是undefined.

  • 对象的解构赋值,可以很方便的将现有的对象赋值给某一个变量。

// 例一
let { log, sin, cos } = Math;

// 例二
const { log } = console;
log('hello') // hello
  1. 第一个例子是将 Math中的 log,sin,cos这三个方法赋值到了对应的变量上,使用起来就非常方便了.
  2. 第二个例子是将console.log赋值给了log变量.
  • 对象的解构赋值是对象的一种简写:
let { foo: foo, bar: bar } = { foo: 'aaa', bar: 'bbb' };
  • 对象的解构赋值的内部机制是先找到同名属性,然后再赋值给对应的变量,真正被赋值的是后者不是前者。
let { foo: baz, } = { foo: 'aaa', bar: 'bbb' };

console.log(baz); // 'aaa';
console.log(foo); // error: foo is not defined

在这里foo是匹配模式,baz才是真正的变量。真正被赋值的是变量baz,而不是匹配模式foo.

嵌套使用

与数组一样,对象解构赋值也可以嵌套。

let obj = {
	p:[
    'Hello',
    {y:'World'}
  ]
};

let { p: [x, { y }] } = obj;
console.log(x); // 'Hello'
console.log(y); // 'World'

注意这里的p是匹配模式,不是变量。

如果要让p也是变量,需要如下写:

let obj = {
	p:[
    'Hello',
    {y:'World'}
  ]
};

let {p, p: [x, { y }] } = obj;
console.log(p); // ['Hello',{y:'World'}]
console.log(x); // 'Hello'
console.log(y); // 'World'
复杂的嵌套
const node = {
  loc: {
    start: {
      line: 1,
      column: 5
    }
  }
}

let {loc, loc: { start }, loc:{ start:{line}} } = node;
console.log(loc); // { start: { line: 1,column: 5 }}
console.log(start); // { line: 1,column: 5 }
console.log(line); // 1

对于上面的代码,第一个是对loc属性的赋值,第二个loc是匹配模式,对start进行赋值,第三个locstart都是匹配模式,对line进行赋值。

  • 当结构模式是嵌套的对象,而且子对象所在的父属性不存在,那么就会报错.
let {foo: { bar }} = {baz: 'baz'};

此时等号左边对象的foo属性对应的是一个子对象,这个子对象中有一个bar属性,解构的时候就会报错。因为这个时候取foo的时候是undefined,再undefined的基础上取子属性就会报错。

  • 对象的解构赋值可以取到继承的属性
const obj1 = {};
const obj2 = { foo: 'bar' };
Object.setPrototypeOf(obj1,obj2);

const {foo} = obj1;
console.log(foo); // 'bar'

对象obj1的原型对象是obj2foo属性不是obj1自己的属性,而是继承于obj2的属性,解构赋值是可以取到这个属性的。

设置默认值

对象的解构赋值也是可以设置默认值的。

var {x = 3} =  {};
console.log(x); // 3

var {x, y = 5} =  {x: 1 };
console.log(x); // 1
console.log(y); // 5

var {x : y = 3} = {};
console.log(y); // 3

var {x: y = 3} = {x: 5};
console.log(y); // 5

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

var {x = 3} = {x: undefined};
console.log(x); // 3

var {x = 3} = {x: null};
console.log(x); // null

需要注意

  1. 如果要将一个已经声明的变量用于解构赋值,需要非常小心大括号{}.
let x;
{x} = {x: 1};

此时,因为{}会构成一个新的代码块,所以这里的代码就会报错,避免报错,需要按照下面的方式写(避免{}位于行首):

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

此时就不会报错,并且可以正常进行解构赋值。

  1. 解构赋值允许等号左边的模式之中,不放置任何变量名,所以就会出现很多奇怪的赋值表达式。
({} = [true, false]);
({} = 'abc');
({} = []);

虽然上面的表达式没有意义,但是是合法的。

  1. 由于数组本质是特殊的对象,所以可以用数组对对象属性进行解构赋值
let arr = [1,2,3];
let {0 : first, [arr.length - 1] : last} =arr;
console.log(first); // 1
console.log(last); // 3

字符串的解构赋值

字符串也可以解构赋值,此时的字符串被转换成了一个类似数组的对象,并且还有一个length的属性,此时也可以根据这个属性进行属性解构赋值。

const [a,b,c,d,e] = 'hello';
let {length: len} = 'hello';
console.log(a); // 'h'
console.log(b); // 'e'
console.log(c); // 'l'
console.log(d); // 'l'
console.log(e); // 'o'
console.log(len); // 5

数值和布尔值的解构赋值

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

let {toString: s} = 123;
console.log(s); // function toString(){}
console.log(s === Number.prototype.toString); // true

let {toString: s} = true;
console.log(s === Boolean.prototype.toString); // true

只要等号的右边的值不是对象或者数,就先将其转换成对象。由于undefinednull无法转换成对象,所以对其进行解构赋值会报错。

函数参数的解构赋值

基本用法

函数的参数也可以解构赋值。

function add([x,y]){
	return x+y;
}

console.log(add([1,2])); // 3

上面add()函数的参数表面上是一个数组,但是在传入参数的时候,数组的参数就被解构成了变量xy.

设置默认值

function move({x = 0, y = 0} = {}){
	return [x, y];
}

console.log({x: 3, y: 8}); // [3, 8]
console.log({x: 3}); 	// [3, 0]
console.log({}); 		// [0, 0]
console.log(); 			// [0, 0]

函数move()的参数是一个对象,通过这个对象进行解构,得到变量x和变量y的值。如果解构赋值失败,则等于默认值。

需要注意

function move({ x, y } = { x: 0, y: 0 }){
	return [x, y];
}

console.log({x: 3, y: 8}); // [3, 8]
console.log({x: 3}); 	// [3, undefined]
console.log({}); 		// [undefined, undefined]
console.log(); 			// [undefined, undefined]

上面是为函数move()的参数设置了默认值,并不是为变量xy指定默认值。所以回到了与之前不一样的结果。

  • undefined会触发函数参数的默认值
[1, undefined, 3].map((x = 'yes') => x);
// [1, 'yes', 3];

圆括号的问题

对于编译器来说,必须解析到(或者解析不到)等号才能知道当前解析的是匹配模式还是表达式。

在ES6中,只要有可能导致解构歧义的就不得使用圆括号

不能使用圆括号的情况

变量声明语句
let [(a)] = [1];
let {x: (c)} = {};
let ({x: c}) = {};
let {(x: c)} = {};
let {(x): c} = {};
let {o : ({p: p})} ={o : {p: 2}};

上面这六种都会报错,在声明变量语句中,匹配模式不能使用圆括号。

函数参数

函数参数也属于变量声明,所以不能带圆括号。

function f([(z)]) {return z;}  // 报错
function f([z, (x)]) {return x;} // 报错
赋值语句的模式
({p: a}) = {p:42}; 		// 报错
([a]) = [5];				// 报错
[({p:1}), {x:c}] =[{},{}];	// 报错

不能将整个模式放在圆括号之中,也不能将部分模式放在圆括号之中。

可以使用圆括号的情况

赋值语句的非模式部分,可以使用圆括号
[(b)] = [3]; 
({p: (d)}) = {};
[(parsnInt.prop)] = [3];

用途

交换变量的值

let x = 1;
let y = 2;

[x,y] = [y,x];
console.log(x);
console.log(y);

上面代码交换变量xy的值,这样写简单、易读、语义清晰。

从函数返回多个值

  • 返回一个数组
function example(){
  return [1,2,3];
}
let [a,b,c] = example();
console.log(a); // 1
console.log(b); // 2
console.log(c); // 3
  • 返回一个对象
function example(){
  return {
    foo: 1,
    bar: 2,
  };
}
let { foo, bar } = example();
console.log(foo); // 1
console.log(bar); // 2

函数参数的定义

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

  • 参数是一组有序的值
function f([x,y,z]){ ... }
f([1,2,3]);
  • 参数是一组无序的值
function f({x,y,z}){ ... }
f({z:3,y:2,x:1});

提取 JSON 数据

let jsonData={
  id: 42,
  status: 'ok',
  data: [555,333]
};
let { id, status, data: number } = jsonData;
console.log(id);     // 42 
console.log(status); // 'ok'
console.log(number); // [555,333]

函数参数的默认值

function ({x = 1, y = 2, z = 3} = {}){}

指定函数饿默认值,就可以避免函数体内部在写判断语句。

遍历 Map 解构

任何具有 Iterator接口的对象,都可以使用for...of循环遍历。配合变量的解构赋值,获取Map解构中的keyvalue就非常方便了。

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 arr = [
      {id: 1, name: 'a'},
      {id: 2, name: 'b'},
      {id: 3, name: 'c'},
    ];
for (const {id, name} of arr) {
      console.log(`${id} is ${name}`);
    }

// 1 is a
// 2 is b
// 3 is c

输入模块的指定方法

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

const { SourceNode } = require('resource-map');

备注:本文是自己学习阮一峰老师的《ECMAScript 6 入门》所做的笔记,大部分例子来源于此书。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值