Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

解构赋值 #12

Open
18888628835 opened this issue Apr 6, 2021 · 0 comments
Open

解构赋值 #12

18888628835 opened this issue Apr 6, 2021 · 0 comments

Comments

@18888628835
Copy link
Owner

解构赋值

数组解构

以前,为变量赋值,只能直接指定值。

let a = 1;
let b = 2;
let c = 3;

现在可以这样

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

还支持嵌套解构

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

留空也是可以的

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

配合rest运算符,也可以解构

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

不成功情况

不成功则为undefined

let [foo] = [];
let [bar, foo] = [1];
foo //undefined

不完全解构

还支持不完全解构

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

限制

唯一的限制就是需要等号右边为部署了iterator接口的数据结构,准确来说就是可以遍历的数据结构。

// 报错
let [foo] = 1;
let [foo] = false;
let [foo] = NaN;
let [foo] = undefined;
let [foo] = null;
let [foo] = {};

由于等号右边的属性都不具备iterator接口,所以上面的解构赋值都会报错。

作为对比,这里放一个set数据结构,由于其具备iterator接口,所以可以采用数组的形式解构赋值

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

对象的解构赋值

对象也可以解构赋值,换一下括号就行。

跟数组解构不同的是,数组解构非常在意顺序,而对象解构则在意的是属性名是否相同。

let { foo, bar } = { foo: 'aaa', bar: 'bbb' };
foo //'aaa'
bar //'bbb'

上面是从对应的对象中取到对应的属性。

变量名和属性名必须是相同的才可以成功。

let props={ foo: 'aaa', bar: 'bbb' }
let foo =props.foo
let bar =props.bar
//简化为
let {foo,bar}=props

如果解构失败,同样也是undefined

let { baz } = { foo: 'aaa', bar: 'bbb' };
baz // undefined

那么如果我一定要属性名和变量名不一样怎么办?

可以这样写。

let { foo:baz } = { foo: 'aaa', bar: 'bbb' };
baz // 'aaa'

对象解构的本质

这实际上也说明其实对象的赋值是这样的

let { foo:foo } = { foo: 'aaa', bar: 'bbb' };

只不过当对象的属性名和其属性值对应的变量名相同时,可以简写成这样

let {foo:foo}
//等于
let {foo}

真正被赋值的是后者,而不是前者。

所以对象的解构赋值的内部机制是先找到同名属性,然后赋值给对应的变量。

let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
baz // "aaa"
foo // error: foo is not defined

上面代码中,foo只是匹配模式,需要找到对应的属性名,而真正赋予的是baz这个变量名。

嵌套数据结构也可以被解构

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

let { loc, loc: { start }, loc: { start: { line }} } = node;
line // 1
loc  // Object {start: Object}
start // Object {line: 1, column: 5}

我们一定要知道的是,属性名只是匹配模式,真正要赋值的是属性值。

所以上面代码中,第一次匹配时,会匹配{loc:loc},所以loc变量是 Object {start: Object}

第二次赋值loc内的start属性对应的start变量,所以start变量是Object {line: 1, column: 5}

第三次赋值同理。

提取原型链属性

对象的解构赋值可以取到原型链上的属性

let a={age:111}
let b=Object.create(a)//把b的__proto__链接到a上
b //{}
let { age }=b
age //111

const obj1 = {};
const obj2 = { foo: 'bar' };
Object.setPrototypeOf(obj1, obj2);//把obj1的__proto__链接到obj2上
const { foo } = obj1;
foo // "bar"

默认值

解构赋值支持默认值形式

// 数组解构赋值
let [foo = true] = [];
foo // true

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

es6内部使用===运算符,判断位置上是否有值,所以只要一个数组成员为undefined,那么默认值就生效。

默认值可以引用其他变量,但是前提是该变量已经声明

let [x = 1, y = x] = [1, 2]; // x=1; y=2
let [x = y, y = 1] = [];  // ReferenceError: y is not defined

最后发生错误是因为x拿y做声明时,y还没有声明。

//对象的解构赋值
var {x = 3} = {};
x // 3

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

同样的,如果对象的属性值严格等于undefined,也会让默认值生效。

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

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

注意点

如果需要将一个已经声明的变量用于解构赋值,需要加上圆括号。

let x;
{x}={x:1}// SyntaxError: syntax error

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

其他解构赋值

字符串的解构赋值

由于字符串也有 iterator接口,所以可以用于解构赋值

const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"

字符串身上的 length 属性也可以被解构出来

let {length : len} = 'hello';
len // 5

数值和布尔值的解构赋值

当等号右边是数值和布尔值时,会被转为对象。

let {toString: s} = 123;
s === Number.prototype.toString // true

let {toString: s} = true;
s === Boolean.prototype.toString // true

解构赋值的规则是,只要等号右边不是对象或者数组,那么就先转化为对象。由于 undefined 和 null 没办法这样做,所以不能对它们进行解构赋值

函数参数的解构赋值

函数参数可以用来解构,这是非常常见的用法。

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]

解构赋值的常见用途

1.可以交换变量的值

let x = 1;
let y = 2;

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

这种方法不但简洁而且易懂。

2.从函数返回多个值

函数如果需要返回多个值,就可以将其放到数组或者对象中,然后使用解构赋值的形式取出。比如 React 的 hooks ,我们一般是这样写的

const [state,setState]=useState('')

这就是因为 useState 返回了一个数组,我们用解构赋值来取值。

3.函数参数定义

解构赋值可以很直观地取出函数的参数

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

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

4.提取 JSON 数据

let jsonData = {
  id: 42,
  status: "OK",
  data: [867, 5309]
};

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

console.log(id, status, number);

5.函数参数默认值

function fn({a=1,b=2}){
  console.log(a)
}
fn({b:3})

6.循环取值

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

7.模块加载取值

const { SourceMapConsumer, SourceNode } = require("source-map");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant