Symbol
ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,前六种是:undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。
基本用法
Symbol 值通过Symbol
函数生成,Symbol
函数前不能使用new
命令,否则会报错。这是因为生成的 Symbol 是一个原始类型的值,不是对象。
let s1 = Symbol();
let s2 = Symbol();
s1 // Symbol()
s2 // Symbol()
在生成 Symbol 值的时候,可以接受一个字符串作为参数,表示对 Symbol 实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分。
let s1 = Symbol('foo');
let s2 = Symbol('bar');
s1 // Symbol(foo)
s2 // Symbol(bar)
description
创建 Symbol 的时候,可以添加一个描述。如果我们想要得到这个描述,我们可以通过 description
属性直接得到 Symbol 的描述。
const sym = Symbol('foo');
sym.description // "foo"
属性名 Symbol
由于每一个 Symbol 值都是不相等的,这意味着 Symbol 值可以作为标识符,用于对象的属性名,就能保证不会出现同名的属性。这对于一个对象由多个模块构成的情况非常有用,能防止某一个键被不小心改写或覆盖。
let mySymbol = Symbol();
// 第一种写法
let a = {};
a[mySymbol] = 'Hello!';
// 第二种写法
let a = {
[mySymbol]: 'Hello!'
};
// 以上写法都得到同样结果
a[mySymbol] // "Hello!"
在使用过程中需要注意的是,当Symbol 值作为对象属性名时,不能用点运算符,点运算符后面总是字符串,所以不会读取mySymbol作为标识名所指代的那个值。
let syObject = {};
syObject[sy] = "kk";
syObject[sy]; // "kk"
syObject.sy; // undefined
Symbol 值作为属性名时,该属性是公有属性不是私有属性,可以在类的外部访问。但是不会出现在 for…in 、 for…of 的循环中,也不会被 Object.keys() 、 Object.getOwnPropertyNames() 返回。
let syObject = {};
syObject[sy] = "kk";
console.log(syObject);
for (let i in syObject) {
console.log(i);
} // 无输出
Object.keys(syObject); // []
如果要读取到一个对象的 Symbol 属性,可以通过 Object.getOwnPropertySymbols()
和 Reflect.ownKeys()
取到。Object.getOwnPropertySymbols()
方法可以获取对象所有 Symbol 属性名。Reflect.ownKeys()
方法可以返回所有类型的键名,包括常规键名和 Symbol 键名。
Object.getOwnPropertySymbols(syObject); // [Symbol(key1)]
Reflect.ownKeys(syObject); // [Symbol(key1)]
Symbol.for(),Symbol.keyFor()
Symbol.for()
与Symbol()
这两种写法,都会生成新的 Symbol。而它们的区别,前者可以重新使用已经生成的 Symbol 值。Symbol.for()
不会每次调用就返回一个新的 Symbol 类型的值,而是会先检查给定的key
是否已经存在,如果不存在才会新建一个值。
Symbol.for("bar") === Symbol.for("bar")
// true
Symbol("bar") === Symbol("bar")
// false
Symbol.keyFor()
方法返回一个已登记的 Symbol 类型值的key
。
let s1 = Symbol.for("foo");
Symbol.keyFor(s1) // "foo"
let s2 = Symbol("foo");
Symbol.keyFor(s2) // undefined
Map对象
ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。Map对象和object对象之间的区别:
- 一个 Object 的键只能是字符串或者 Symbols,但一个 Map 的键可以是任意值。
- Map 中的键值是有序的(FIFO 原则),而添加到对象中的键则不是。
- Map 的键值对个数可以从 size 属性获取,而 Object 的键值对个数只能手动计算。
- Object 都有自己的原型,原型链上的键名有可能和你自己在对象上的设置的键名产生冲突。
在创建 Map对象的时候,可以使用无参构造创建,也可以传入一个数组作为参数,会自动将数组转化为Map 对象格式的数据。
const m = new Map();
const map = new Map([
['name', '张三'],
['title', 'Author']
]);
不仅仅是数组,任何具有 Iterator 接口、且每个成员都是一个双元素的数组的数据结构都可以当作Map构造函数的参数。Set
和Map
都可以用来生成新的 Map
属性和操作方法
size
属性返回 Map 结构的成员总数。set
方法设置键名key
对应的键值为value
,然后返回整个 Map 结构。如果key
已经有值,则键值会被更新,否则就新生成该键。set
方法返回的是当前的Map对象,因此可以采用链式写法。get
方法读取key
对应的键值,如果找不到key
,返回undefinedhas
方法返回一个布尔值,表示某个键是否在当前 Map 对象之中。delete
方法删除某个键,返回true。如果删除失败,返回false。clear
方法清除所有成员,没有返回值。
const map = new Map();
map.set('foo', a);
map.set('bar', b).set('bar', c);
map.size // 2
map.get('bar') //c
map.has('bar'); //true
map.delete('bar');//true
map.has('bar'); //false
map.clear('bar');
map.has('bar'); //false
遍历方法
- Map.prototype.keys():返回键名的遍历器。
- Map.prototype.values():返回键值的遍历器。
- Map.prototype.entries():返回所有成员的遍历器。
- Map.prototype.forEach():遍历 Map 的所有成员。
const map = new Map([
['F', 'no'],
['T', 'yes'],
]);
for (let key of map.keys()) {
console.log(key);
}
// "F"
// "T"
for (let value of map.values()) {
console.log(value);
}
// "no"
// "yes"
for (let item of map.entries()) {
console.log(item[0], item[1]);
}
// "F" "no"
// "T" "yes"
// 或者
for (let [key, value] of map.entries()) {
console.log(key, value);
}
// "F" "no"
// "T" "yes"
// 等同于使用map.entries()
for (let [key, value] of map) {
console.log(key, value);
}
// "F" "no"
// "T" "yes"
Map 与数组互换互相转换
const myMap = new Map()
.set(true, 7)
.set({foo: 3}, ['abc']);
[...myMap]
// [ [ true, 7 ], [ { foo: 3 }, [ 'abc' ] ] ]
new Map([
[true, 7],
[{foo: 3}, ['abc']]
])
// Map {
// true => 7,
// Object {foo: 3} => ['abc']
// }
Set对象
Set对象类似于数组,但是成员的值都是唯一的,没有重复的值,可以存储任何类型的唯一值,无论是原始值或者是对象引用。
在生成 Set 对象时,可以传入一个数组(或者具有 iterable 接口的其他数据结构)作为参数,用来初始化
//无参构造
const s = new Set();
[2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));
for (let i of s) {
console.log(i);
}
// 2 3 5 4
//有参构造
const set = new Set([1, 2, 3, 4, 4]);
[...set]
// [1, 2, 3, 4]
属性和方法
size
属性返回 Set 实例的成员总数。add(value)
添加某个值,返回 Set 结构本身。delete(value)
删除某个值,返回一个布尔值,表示删除是否成功。has(value)
返回一个布尔值,表示该值是否为Set的成员。clear()
清除所有成员,没有返回值。
s.add(1).add(2).add(2);
// 注意2被加入了两次
s.size // 2
s.has(1) // true
s.has(2) // true
s.has(3) // false
s.delete(2);
s.has(2) // false
遍历方法
- keys():返回键名的遍历器
- values():返回键值的遍历器
- entries():返回键值对的遍历器
- forEach():使用回调函数遍历每个成员
let set = new Set(['red', 'green', 'blue']);
for (let item of set.keys()) {
console.log(item);
}
// red
// green
// blue
for (let item of set.values()) {
console.log(item);
}
// red
// green
// blue
for (let item of set.entries()) {
console.log(item);
}
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]
let set = new Set([1, 4, 9]);
set.forEach((value, key) => console.log(key + ' : ' + value))
// 1 : 1
// 4 : 4
// 9 : 9
也可以使用 for…of 循环遍历 Set
let set = new Set(['red', 'green', 'blue']);
for (let x of set) {
console.log(x);
}
// red
// green
// blue