数组的结构赋值
定义:从数组和对象中提取值,对变量进行赋值,这被称为解构
let = [a, b, c, d] = [1, 2, 3];
// a = 1;
// b = 2;
// c = 3;
// d = undefined;
以上为例,可以从数组中提取值,按照对应位置,对变量赋值,如果解构不成功,变量的值就等于undefined
;只要等号两边的模式相同,左边的变量就会被赋予对应的值。
let [x, y] = [1, 2, 3];
x // 1;
y // 2
如果等号的右边不是数组,那么会报错,这就是不完全解耦
let [foo] = 1;
...
对于Set
结构,也可以使用数组的解构赋值。
let [x, y, z] = new Set(['a', 'b', 'c']);
x // 'a'
y // 'b'
z // 'c'
只要某种数据结构上具有Iterator
接口,都可以采用数组形式的解构赋值。
默认值:解构赋值允许指定默认值。
let [foo = true] = [];
foo // true
let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'
注意:ES6内部使用严格相等运算符(===),判断一个位置是否有值。所以只有当一个数组成员严格等于undefined
,默认值才会生效。
let [x = 1] = [undefined];
x // 1
let [x = 1] = [null];
x // null
上面代码中,如果一个数组成员是null, 默认值不会生效,因为null不严格等于undefined。
默认值是表达式:表达式是惰性求值,只有在用到的时候,才会去求值。
function f() {
console.log('aaa');
}
let [x = f()] = [1];
console.log(x); // 1
等价于
let x;
if ([1][0] === undefined) {
x = f();
} else {
x = [1][0];
}
// x = 1
默认值可以引用解构赋值的其他变量,但是该变量必须已经声明
// x,y 已声明变量
let [x = 1, y = x] = []; // x=1; y=1
// 未声明变量y
let [x = y, y = 1] = []; // ReferenceError: y is not defined
对象的解构赋值
对象的属性没有次序,变量必须与属性同名,才能取到正确的值
let { bar, foo } = { foo: 'aaa', bar: 'bbb' };
foo // "aaa"
bar // "bbb"
let { baz } = { foo: 'aaa', bar: 'bbb' };
baz // undefined
对象的解构赋值,可以将现有对象的方法,赋值到某个变量上,使用起来会很方便
// 例一
let { log, sin, cos } = Math;
// 例二
const { log } = console;
log('hello') // hello
如果变量名与属性名不一致,必须写成下面这样
let obj = { first: 'hello', last: 'world' };
let { first: f, last: l } = obj;
f // 'hello'
l // 'world'
对象的解构赋值的内部机制:是先找到同名属性,然后在赋给对应的变量。真正被赋值的是后者。
let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
baz // "aaa"
foo // error: foo is not defined
上面代码中,真正被赋值的是baz
,foo
只是匹配的模式。
解构也可以用于嵌套结构的对象
let obj = {};
let arr = [];
({ foo: obj.prop, bar: arr[0] } = { foo: 123, bar: true });
obj // {prop:123}
arr // [true]
解构模式是嵌套的对象时,子对象所在的父属性必须存在,否则报错。
// 报错
let {foo: {bar}} = {baz: 'baz'};
foo
属性对应一个子对象,值为undefined
。该子对象的bar
属性,解构时会报错。此时父属性值为undefined
,再取子属性则会报错
默认值
对象的解构也可以指定默认值
var {x, y = 5} = {x: 1};
x // 1
y // 5
默认值生效的条件是:对象的属性值严格等于undefined
var {x = 3} = {x: undefined};
x // 3
注意点
- 将已经声明的变量用于解构赋值的正确写法
// 正确的写法
let x;
({x} = {x: 1});
- 解构赋值允许等号左边的模式之中不放任何变量名,但是这种表达式毫无意义
({} = [true, false])
- 可以对数组进行对象属性的解构
let arr = [1, 2, 3];
let {0 : first, [arr.length - 1] : last} = arr;
first // 1
last // 3
数组中0
的键对应的值是1
,[arr.length - 1]
就是2
键,对应的值是3
数值和布尔值的解构赋值的规则:只要等号右边的值不是对象或数组,就先将其转为对象。undefined
和null
无法转为对象,所以对他们进行解构赋值都会报错。
用途
-
交换变量的值
let x = 1; let y = 2; [x, y] = [y, x];
-
从函数返回多个值
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数据
解构赋值对提取 JSON 对象中的数据,尤其有用。
let jsonData = {
id: 42,
status: "OK",
data: [867, 5309]
};
let { id, status, data: number } = jsonData;
console.log(id, status, number);
// 42, "OK", [867, 5309]
上面代码可以快速提取 JSON 数据的值。
- 函数参数的默认值
jQuery.ajax = function (url, {
async = true,
beforeSend = function () {},
cache = true,
complete = function () {},
crossDomain = false,
global = true,
// ... more config
} = {}) {
// ... do stuff
};
指定参数的默认值,就避免了在函数体内部再写var foo = config.foo || ‘default foo’;这样的语句。
- 遍历Map结构
任何部署了 Iterator 接口的对象,都可以用for…of循环遍历。Map 结构原生支持 Iterator 接口,配合变量的解构赋值,获取键名和键值就非常方便。
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
如果只想获取键名,或者只想获取键值,可以写成下面这样。
// 获取键名
for (let [key] of map) {
// ...
}
// 获取键值
for (let [,value] of map) {
// ...
}
- 输入模块的指定方法
加载模块时,往往需要指定输入哪些方法。解构赋值使得输入语句非常清晰。
const { SourceMapConsumer, SourceNode } = require("source-map");
Symbol
表示独一无二的值。
它是第七种数据类型:Undefined、Null、boolean、Number、String、Object、symbol.
特点:
不能使用new命令(报错,因为生成的Symbol是一个原始类型的值不是对象。)
不能添加属性。
参数;
可以接受一个字符串作为参数(对Symbol实例的描述,容易区分)
let s1 = Symbol( ‘foo’ );
可以接受一个对象
const obj = {
toString() {
return ‘abc’
}
};
const sym = Symbol( obj );
如果参数是一个对象,就会调用该对象的toString()方法,先转化成字符串,然后才会生成一个Symbol值
参数只是表示对当前Symbol值的描述,因此相同参数的Symbol函数的返回值是不相等的(参数相同,返回值不相等)
Symbol值不能与其他类型的值进行运算,会报错。
Symbol值可以显示转为字符串;(使用toString() 方法)
Symbol值也可以转为布尔值(Boolean(sym)),但是不能转换为数值
读取Symbol描述的方法:
将Symbol显示的转换成字符串
使用实例属性description,直接返回Symbol的描述
Symbol值可以作为标识符,用于对象的属性名,不会出现同名的属性
适用于:一个对象多个模块构成的情况,防改写或覆盖
Symbol值作为对象属性名时,不能用点运算符(因为点运算符后面总是字符串,所以不会读取Symbol作为标识符所指的那个值,导致属性名变成了一个字符串,而不是Symbol值)
Symbol值定义属性时,Symbol值必须放在方括号之中,否则会被错认为是字符串,而不是Symbol值
Symbol可以定义一组常量,保证常量的值都是不相等的
Symbol的优点:
任何值都不可能有相同的值
Symbol值作为属性名时,该属性是公开属性不是私有属性
获取指定属性名
Object.getOwnPropertySymbols(): 获取指定对象的所有Symbol属性名,返回一个数组
获取所有类型的键名
Reflect.ownKeys(): 返回所有类型的键名,包扩常规键名和Symbol键名
Symbol 用作私有属性
Symbol.for(): 使用同一个Symbol值,会被登记在全局环境中
Symbol.for()与Symbol()这两种写法,都会生成新的 Symbol。它们的区别是,前者会被登记在全局环境中供搜索,后者不会。Symbol.for()不会每次调用就返回一个新的 Symbol 类型的值,而是会先检查给定的key是否已经存在,如果不存在才会新建一个值
由于Symbol()写法没有登记机制,所以每次调用都会返回一个不同的值。
Set
基本用法
Set数据结构类似数组,但是值是唯一的,没有重复的值
Set本身是一个构造函数,用来生成Set数据结构
参数:
Set函数可以接受一个数组作为参数(或者具有iterable接口的其他数据结构)用来初始化
https://es6.ruanyifeng.com/