变量的解构赋值

2018-02-04  本文已影响0人  阿go

ES6允许按照一定的模式从数组或者对象中提取值,然后对变量进行赋值,这种方式成为解构赋值,这使得在平时的编程中变得高效简洁,解构赋值有以下内容:

数组的解构赋值

1、基本用法

//es6以前
var a = 1;
var b = 2;
var c = 3;
// es6
let [a,b,c] = [1,2,3]
//可得到
a // 1
b // 2
c // 3

由以上的赋值是从数组中提取值,然后【按照对应位置对变量进行赋值】
其本质上属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。
如下例子:

let [foo,[[bar],baz]] = [1,[[2],3]];
foo // 1
bar // 2
baz //3
//即以上的的等号两边的模式相同,可以进行对应位置变量的赋值
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] = [1]
x // 1
y // undefined
z // undefined
// 这种情况属于解构不成功,所以未解构到的变量会等于undfined
let [x,y] = [1,2,3]
x // 1
y // 2
//这种情况属于不完全解构,即等号左边的模式只匹配一部分的等号右边的数组,但这种情况依然是解构成功
但是,如果等号的右边不是数组(严格来说不是可遍历的结构),那么将会报错
let [a] = 1;
let [b] = false;
let [c] = NaN;
let [d] = undefined;
let [e] = null;
let [f] = {};
//以上语句都会报错,因为等号右边的值或者是转为对象以后不具备Iterator接口(前5个表达式)
//或者本身不具备Iterator接口(最后一个表达式)

2、默认值

解构赋值允许制定默认值,即es6内部会严格使用相等运算符(===)判断一个位置是否有真值,所以如果一个数组成员不严格等于undefined,默认值是不会生效的,反之,默认值生效。

let [x = 1] = [undefined];
x // 1

let [ y = 2] = [];
y // 2

let [z = 3] = [null];
z // null
// 此时因为null不严格等于undefined
// 如果默认值是一个表达式,那么这个表达式是惰性求值,即只有在用到时才会求值
function f() {
    console.log(111);
}
let [x = f()] = [1];//此时,因为x能够取到值1,所以函数f根部不会执行


以上的解构赋值代码等价于于:
let x;
if([1][0] === undefined) {
    x = f();
}else{
    x = [1][0];
}
//默认值也可以引用解构赋值的其他变量,但必须保证在引用前,该变量以声明
let [x = 1,y = 2] = []; // x=1,y=2
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] = []; // ReferenceError
最后一个表达式会报错,以为x在用到默认值y时,y还没有声明

2、对象的解构赋值

对象的解构赋值与数组有一个重要的区别,即数组的元素时按次序排列的,变量的取值是由它的位置决定的;而对象的属性没有次序,变量必须与属性同名才能取到正确的值。
对象的解构赋值的内部机制是先找到同名属性,然后再复制给对应的变量,真正被赋值的是后者而不是前者,见如下栗子:

let {bar,foo} = {foo:1,bar:2};
foo // 1
bar // 2

//以上赋值等价于下面代码,即当变量名与属性名相同时,可简写
let {bar:bar,foo:foo} = {foo:1,bar:2};
foo //1
bar // 2

//如果变量名与属性名不一样时,需要写全:
let {foo:baz} = {foo:3,bar: 4};
baz // 3
foo // error: foo is not defined
// 上面代码中,foo时匹配的模式,baz才是变量,真正被复制的是变量baz,而不是模式foo
//解构同样也可以用于嵌套结构的对象
let = {
    p: [
        'hello',
        {y: 'world'}
    ]
};
let {p:[x,{y}]} = obj;
x // hello
y // world
//在此处,p是模式不是变量,因此不会被赋值
对象的解构也可以指定默认值

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

let {x = 3} = {};
x // 3

let {x,y=5} = {x :1};
x // 1
y // 5

let {x:y = 5} = {};
y //5

let {x:y=5} = {x:3};
y // 3

let {x = 3} = {x:null};
x // null
注意:如果要将一个已经声明的变量用于解构赋值,必须非常小心
let x;
{x} = {x:1}; //SyntaxError: syntax error
//以上的代码报错,是因为javascript引擎会将{x}理解成一个代码块,从而发生了语法错误
//只有不将大括号写在行首,避免javascript将其解释为代码块,才能解决这个问题

//正确写法
let x;
({x}) = {x:1};
x // 1

3、字符串的解构赋值

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

let [a,b,c] = 'god';
a // g
b // o
c // d
//类数组的对象都有一个属性length,因此还可以对这个属性进行解构赋值
let {length:len} = 'hello';
len // 5

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

解构赋值时,如果等号右边是数值和布尔值,则会先转为对象。

let {toString:s} = 123;
s === Number.prototype.toString   // true
//数值的包装对象都有toString属性,因此变量s都能取到值。

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

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

5、函数参数的解构赋值

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

// 函数add的参数表面上是一个数组,但在传入参数的那一刻,数组参数就被解构成x和y;
//所以对于函数内部的代码来说,它们能感受到的参数就是x和y。
function add([x,y]) {
    return x + y;
}
add([1,2]) // 3

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

// 如果结构失败,则变量等于默认值
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]
注意,一下写法则会得到不一样的结果
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指定默认值,所以会的得到不一样的结果。

6、圆括号问题

解构赋值虽然方便,但解析起来并不容易,对于编译器来说,一个式子到底是模式还是表达式,没有办法一开始就知道,必须解析到(或解析不到)等号才能知道。
由此带来的问题是,如果模式中出现圆括号该怎么处理?ES6的规则是:之哟啊有可能导致结构有歧义,就不得使用圆括号。

不能使用圆括号的情况

1、变量声明语句

// 一下全部报错
let [(a)] = [1];
let {x:(c)} = {};
let {(x:c)} = {};
let ({x:c}) = {};
let {(x):c} = {};
let {o: ({p:p})} = {o:{p:2}};
//上面6个语句都会报错,因为他们都是变量声明语句,模式不能使用圆括号。

2、函数参数
函数参数也属于变量声明,因此不能使用圆括号

function f([(a)]) {return a}
function f([z,(x)]) {return x}
// 以上都会报错

3、赋值语句的模式

({p:a}) = {p:1}
([a]) = [5];
// 以上都会报错
可以使用圆括号的情况

可以使用圆括号的情况只有一种:赋值语句的飞魔士部分可以使用圆括号

// 以下使用正确
[(b)] = [3];
({p:(d)} = {p:3});
//以上语句都可以正确执行,因为都是赋值语句而不是声明语句。

7、变量解构赋值的用途

主要可以适用在以下几种情况:
1、交换变量

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

2、从函数返回多个值

function f(){
     return [1,2,3];
}
let [a,b,c] = f();

3、函数参数的定义
4.提取JSON数据
5、函数参数的默认值
6、遍历Map解构
7、输入模块的制定方法

const {a,b} = require('xxx.js')
上一篇下一篇

猜你喜欢

热点阅读