重学ES6(二)变量的解构赋值

229 阅读5分钟

简单介绍

ES6 语法中,允许按照规定的方式,从数组和对象中提取值,来对变量进行赋值,称之为解构。

ES5中,我们给变量赋值都是通过 var 定义的方式

var a = 1
var arr = []
var obj = {}

详细分析

数组的解构赋值

在ES6中只要数据有 Iterator 接口,都可以进行解构赋值。Iterator(遍历器)也是一个在 ES6 中很神奇的东西,其实它在很多语言中也有,这个我们会在后面分析的,我们这里就对于有 Iterator 接口的,都可以进行解构赋值就行。

eg1:

// ES6
let [a, b, c] = [1, 2, 3]

// ES5
var a = 1,
    b = 2,
    c = 3;

上面就是将数组进行解构,可以从数组中提取值,按照对应位置,对变量进行一个赋值操作,换成 ES5 大家就很明显可以理解了。

在其他教程上都对于这种写法,认为是一个模式匹配,只要等号两边的模式相同,左边的变量就会被赋予右边对应的值。

eg2:

// 模式匹配
let [a, [[b], c]] = [1, [[2], 3]]
// a = 1 b = 2 c = 3

// 省略解构
let [, a, b] = [1, 2, 3, 4]
// a =2 b = 3

// 含剩余参数的解构
let [a, ...rest] = [1, 2, 3, 4, 5]
// var a = 1, rest = [2, 3, 4, 5];

//剩余参数没有值的时候
let [a,...reset] = [1]

var _ref = [1],
  a = _ref[0],
  reset = _ref.slice(1);
  
// 备注: 获取剩余数值时候,后面没有值的时候,返回的是个空数组
// 下面转为 ES5 也可以看到,将数组第一个 slice 了

当非数组用数组的方式解构的时候,即等号右边不是数组,且不是可遍历的结构,都会报错

eg:3

let [foo] = 1			// Uncaught TypeError: 1 is not iterable
let [foo1] = false		// Uncaught TypeError: false is not iterable
let [foo2] = undefined 	// Uncaught TypeError: undefined is not iterable
let [foo3] = null		// Uncaught TypeError: null is not iterable
let [foo4] = {}			// Uncaught TypeError: {} is not iterable
let [foo5] = NaN		// Uncaught TypeError: NaN is not iterable

// 以上都会报错

因为上面转成 ES5 都能知道了

var _ = 1,
	foo = _[0];//报错
// 其他都是同理

set的解构赋值

ES6 提供了新的数据结构 Set,类似于数组的一个新的数据解构。set对象是值的集合,元素只会出现一次,即Set中的元素是唯一的。

eg1:

let [x, y] = new Set([1, 2])
// x => 1
// y => 2

Generator 函数解构

Generator 函数后面我们会单独写一章来分析的,这里我们就知道 Generator 函数是有 Iterator 的,所以是可以 for of,或者说是可以解构的。

eg1:

function* fun(){
    let a = 0;
    let b = 1;
    while(true){
        yield a;
        [a,b] = [b,a+b];
    }
}
let [first,second,third,fourth,fifth,sixth] = fun();
console.log(sixth); //结果为5	

解构时的默认值

eg1:

// ES6
let [a=3, b = 5] = [undefined, 4]

// a => 3
// b => 5

// ES5
var _undefined = undefined,
    a = _undefined === void 0 ? 3 : _undefined,
    _ = 4,
    b = _ === void 0 ? 5 : _;

下面这段转为 ES5,其实就是多了是否为 undefined 的判断,如果对应的右边是 undefined,但是左右有默认值,则变量还是为默认值。

对象的解构赋值

数组因为是线性的结构,所以数组的解构是按次序排列来的,变量取值是按照对应位置来的。而对象的属性是没有顺序的,所以对象解构的时候,必须与对象的属性同名才能取到正确的值。

eg1:

// ES6
let { a, b } = { a: 1, b: 2 }
// a => 1
// b => 2

// ES5
var _a$b = {
  a: 1,
  b: 2
},
    a = _a$b.a,
    b = _a$b.b;

实际上就是将对象里面对应属性名的值赋给解构中的变量。

eg2:

// ES6
let {a: c, b: d} = {a: 3, b: 4}
a => undefined
b => undefined
c => 3
d => 4

// ES5
var _a$b = {
  a: 3,
  b: 4
},
  c = _a$b.b,
  d = _a$b.a;

在上面 ES6 中的解构,可以看到,其中 a 和 b 是 undefined,而对应的 ES5 中,其实就是对 c 和 d 进行了对应位置的赋值。

所以,对象的解构内部机制应该是先找到同名的属性,然后再赋予相应的变量,真正被赋予的是前一个对象的后一个变量,即上面的 c 和 d,而不是左边对象的模式,即 a 和 b。

eg3:

// ES6
let e = {a: c, b: d} = {a: 3, b: 4}
e.a => 3
e.b => 4
c => 3
d => 4
a => undefined
b => undefined

对象解构中的括号问题

eg1:

let a
[a] = [3]
a => 3

eg2:

let a
{ a } = { a: 3 }
// Uncaught SyntaxError: Unexpected token '='

对于这里数组解构和对象解构,数组是可以完成解构的,但是对象会报错。因为 [a] 再js引擎中,只会理解为成一个数组。但是对于 {a},js引擎可能理解成一个代码块或者理解成一个对象。现在对于行首是 {} 的,全部都理解为代码块,所以要让js引擎不认为这是代码块。

eg3:

let a
({a} = {a: 3})	

这里用括号将 {} 包起来的,都是表达式。这样再表达式内部,{} 就可以被认为是一个对象了,就可以顺利的解构了。

字符串的解构赋值

let [a, b, c] = '123'
// a => 1
// b => 2
// c => 3
typeof a => string 字符串解构出来还是字符串

对于字符串的长度

let {length: s} = '123'
// s => 3
// typeof s => number

这里 length 是为了和字符串中属性 length 对应,然后对应长度值赋值给 s,得到字符串长度。

函数参数的解构赋值

function test([x, y]) {
	console.log(x, y)
}
test([1, 2])	// 1, 2

总结

以上就是对于解构赋值的一些学习笔记,解构赋值在 ES6 开发过程中能使代码更优雅,我们在获取对象或者对应数组各类取值,都可以使用这种方式来拿到值,不需要再使用之前 var 定义一堆变量了,并且取不到的变量也默认定义后是 undefined 了。对于解构赋值大家如果还有更多见解,欢迎评论讨论呀。