ES6教程学习(二)---变量的解构赋值

101 阅读7分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第2天,点击查看活动详情

数组的解构赋值

用法

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

// 以前
let a = 1
let b = 2
let c = 3
console.log(a, b, c)//1 2 3

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

按照对应位置,从数组中提取值,对变量赋值,这种写法属于模式匹配。只要等号两边的模式相同,左边的变量就会被赋予对应的值。

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

let [d,e,f] = [4]
console.log(d, e, f) //4 undefined undefined

let [g,h,...r] = [5]
console.log(g,h,r) //5 undefined []

let [k,,l] = [6, 7, 8]
console.log(k,l) //6 8

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

不完全结构

属于不完全结构,这种情况下,也会解构成功

let [a,b,c] = [1,2]
console.log(a, b)//1 2

let [d,[e,f],g] = [3,[4],5]
console.log(d,e,f)//3 4

如果等号右边不是数组,那么就会报错

let [a] = 1//1 is not iterable
let [b] = false//false is not iterable
let [c] = undefined//undefined is not iterable
let [d] = {}// {} is not iterable
let [e] = null//null is not iterable
let [f] = NaN //NaN is not iterable

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

let [a, b, c] = new Set([1, 2, 3])
console.log(a, b, c) //1 2 3

generotor函数

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

默认值

解构赋值允许指定默认值

let [a=true] = []
console.log(a) //true

let [b,c=2] = [1]
console.log(b, c) //1,2

let [d,e=3] = [4,undefined]
console.log(d,e)//4,3

注意:ES6内部严格使用undefined,判断一个位置是否有值。所有只有一个值严格等于unedfined,默认值才会生效。如果一个数组成员是null,默认就不会生效,因为null不严格等于undefined。

let [a=1] = [null]
console.log(a) //null
let [b=2] = [undefined]
console.log(b) //2

如果默认值是一个表达式,是惰性的,在用到他的时候才会执行。

function f(){
   console.log('来了')
}
let [a=f()]=[1]
console.log(a) //1

function f(){
        console.log('来了')
}
let [a=f()]=[undefined]
console.log(a) //来了 undefined

默认值可以解构赋值的其他变量,前提是必须这个变量已经被解构

let [a=1,b=a] = []// a=1 b=1
let [a=1,b=a] = [2]//a=2 b=2
let [a=1,b=a] = [3,4] //a=3 b=3
let [a=b,a=1] //ReferenceError: a is not defined

对象的解构赋值

对象和数组的解构赋值不同点:数组是按照顺序排列;对象取值没有顺序之分,变量与属性名相同就可取到值。

let {a,b} = {a:1,b:2}
console.log(a, b) // 1,2

如果对象没有解构到值,则为undefined

let {a,b,c} = {a:1,b:2}
console.log(c) // undefined

将现有对象的方法赋值到某个变量上

const {log} = console
log('hello')//hello

变量值和属性值不一样,处理方法如下

let {a:b}={a:1}
console.log(b)//1

对象赋值的内部机制,先找到同名属性,然后再赋值给赋值给对应的变量。真正被赋值的是后者,并不是前者。对象解构赋值的简写:let {a:a,b:b} = {a:1,b:2}

对象嵌套的解构赋值

let obj = {
        p:[
                'hello',
                {
                        y:'world'
                }
        ]
}

let {p:[x,{y}]} = obj
console.log(x, y)//hello world
console.log(p)// ReferenceError: p is not defined

注意:p是模式不是变量,因此不会被解构赋值。应写成这样:

let obj = {
        p:[
                'hello',
                {
                        y:'world'
                }
        ]
}

let {p:[x,{y}]} = obj
console.log(x, y)//hello
console.log(p)// ['hello',{y:world}]
let a = {
        b:{
                c:{
                        d:1,
                        e:2
                }
        }
}
let {b,b:{c},b:{c:{d}}} = a
console.log(b)//c{d:1,e:2}
console.log(c) //{d:1,e:2}
console.log(d)//1

以上,b,c是模式,d是变量

以下是嵌套赋值:

let obj = {};
let arr = [];

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

console.log(obj, arr)////{prop:123} [true]

等号左边对象的a属性,对应一个子对象。该子对象的b属性,解构时会报错。因为a属性的值为undefined,再取子属性就会报错。

//报错	
let {a: {b}} = {c: 'baz'};
console.log(a) //Cannot read properties of undefined (reading 'b')

对象的解构赋值可以取到继承属性

let a = {}
let b = {aa:1}
Object.setPrototypeOf(a, b)

console.log(a, aa.aa)//{} 1

默认值

let {a=1}={}
console.log(a)//1

let {b,c=2} = {b:3}
console.log(b);//3

let {d:e=4}={d:5}
console.log(e)//5

默认值生效的条件是,对象的属性值严格等于undefined。null和undefined不严格相等

let {a=3}={a:undefined}
console.log(a)//3
let {b=4}={b:null}
console.log(b)//null

注意:

a.如果将一个已经声明的变量解构赋值,需注意

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

let a;
{a}={a:1} //SyntaxError: Unexpected token '='
({a}={a:1}) //1解决办法
console.log(a)//1

因为JS引擎会将{x}理解成一个代码块,从而发生语法错误。只有不将大括号写在行首,避免javascript将其理解为代码块,才能解决这个问题。

b.解构赋值允许左边的模式之中,不放置任何变量名。因此可以写出非常古怪的赋值表达式。虽然表达式毫无意义,但是语法是合法的,可以执行。

({}=[true,false]);
({}='abc');
({}=[]);

c.由于数组是特殊的对象,因此可以对数组进行对象属性的解构。解构的是下标。

let arr = [1, 2, 3];
let {0:first, [arr.length-2]:last} = arr
console.log(first)//1
console.log(last)//2

字符串的解构赋值

字符串也可以进行解构赋值,因此,字符串被转换成类似数组的对象。

const [a,b,c,d,e] = 'hello'
console.log(a,b,c,d,e)

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

const {length:len} = 'hello'
console.log(len)//5

数值和布尔值的解构赋值

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

let {toString:s} = 123
//Number === Number.proptotype.toString
console.log(s) //ƒ toString() { [native code] }
let {toString:a} = true
console.log(a) //ƒ toString() { [native code] }

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

let {toString:s} = undefined
console.log(s) //Cannot destructure property 'toString' of 'undefined' as it is undefined.

函数参数的解构赋值

let b = [[1, 2], [3, 4]].map(([a,b]) =>{
        return a+b
})
console.log(b) //3 7

函数参数的解构也可以使用默认值

function add({a=1, b=2}={}){
   return [a, b]
}
console.log(add({a:3, b:4}))//[3, 4]
console.log(add({a:9})) //[9, 2]
console.log(add()) //[1, 2]
console.log(add({})) //[1, 2]

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

function add({a,b} = {a:1,b:2}) {
   return [a, b]
}
console.log(add()) //[1,2]
console.log(add({a:3})) //[3,undefined]
console.log(add({}))//[undefined, undefined]
console.log(add({a:3, b:4}))

undefined会触发函数的默认值

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

圆括号问题

ES6的规则是,只要有可能导致歧义的,就不得使用圆括号。但是,建议只要有可能,就不要在模式中防止圆括号。

用途

变量的解构赋值用途很多

  1. 交换变量的值.[x,y]=[y,x]
  2. 从函数中返回多个值
function add(){
        return [1, 2, 3, 4]
}
const [a,b,c,d]=add();
console.log(a,b,c,d)// 1 2 3 4

function sum(){
        return {obj1:'hello', obj2:'world'}
}
const {obj1,obj2} = sum()
console.log(obj1, obj2) //hello world
  1. 函数参数的定义

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

// 参数有次序
function fn([x,y,z]){}
fn([1,2,3])

// 参数是一组无次序的值
function fn({x,y,z}){}
fn({z:3, x:1, y:2})
  1. 提取json中的数据
let jsonData = {
    id: 42,
    status: 'ok',
    data: [1, 2]
}
const {id,status,data:number} = jsonData
console.log(id, status, number)//42 'ok' [1, 2]
  1. 函数参数的默认值function fn(a=1){}
  2. 便利Map结构 任何部署了Iterator接口的对象,都可以用for...of遍历循环。map结构原生支持Iterator接口,配合变量的解构赋值,获取键名和键值就更加方便了
const map = new Map()
map.set('a',1)
map.set('b', 2)
for(let [key, value] of map){
        console.log(key+' is '+value)
}
// 循环value值
for(let [,value] of map){
        console.log(value)//1 2
}
  1. 输入模块的指定方法 加载模块时,往往需要指定哪些方法。解构赋值使得输入语句非常清晰。
import {aa, bb} from 'aa.js'