理解 JavaScript(ECMAScript 6)—— Sets 与 Maps

一、ECMAScript 5 中的 Sets 和 Maps

在 ECMAScript 5 中,通常使用对象属性来模拟 set 和 map 数据类型:

1
2
3
4
5
6
7
var set = Object.create(null)

set.foo = true

if (set.foo) {
// code to execute
}

用对象属性作为 set 中的非重复键值,将值设置为 true 表明其存在性。

1
2
3
4
5
var map = Object.create(null)
map.foo = "bar"

var value = map.foo
console.log(value) // bar

不同于 set,map 常常需要获取其中保存的数据,而不仅仅用于检查 key 是否存在。

存在的问题

用对象作为 set 和 map 适用于简单的场景,在某些情况下对象属性会显露出一定的局限性。
比如所有的对象属性都必须是字符串类型,因此同一个对象中绝不能包含多个(转换成字符串后)一致的键。

1
2
3
4
var map = Object.create(null)
map[5] = "foo"

console.log(map["5"]) // foo

数字类型的键 5 实际上在内部会被转换为字符串 "5",因此 map[5]map["5"] 指向的是同一个值。

又比如:

1
2
3
4
5
6
var map = Object.create(null)
var key1 = {}
var key2 = {}

map[key1] = "foo"
console.log(map[key2]) // foo

上面代码中的 map[key1]map[key2] 也是指向同一值的。因为对象 key1key2 转换为字符串后的值同为 "[object Object]"

二、ECMAScript 6 中的 Sets

ECMAScript 6 中添加了 Set 类型,即一个包含不重复的有序值的列表。

1
2
3
4
5
6
7
8
9
10
11
12
13
let set = new Set()
set.add(5)
set.add("5")

console.log(set) // Set { 5, '5' }

let set2 = new Set()
let key1 = {}
let key2 = {}

set2.add(key1)
set2.add(key2)
console.log(set2.size) // 2

使用 set 去重

1
2
3
4
5
6
7
8
9
10
11
12
let set = new Set()
set.add(5)
set.add("5")
set.add(5)

console.log(set) // Set { 5, '5' }

let set2 = new Set([1, 2, 3, 4, 5, 5, 5, 5])
console.log(set2) // Set { 1, 2, 3, 4, 5 }

console.log(set2.has(2)) // true
console.log(set2.has(6)) // false

forEach

1
2
3
4
5
6
7
8
9
10
11
let set = new Set([1, 2])

set.forEach(function(value, key, ownerSet) {
console.log(key + " " + value)
console.log(ownerSet === set)
})

// 1 1
// true
// 2 2
// true

如果需要在 forEach() 的回调函数中使用 this,可以将 this 作为 forEach() 的第二个参数传递:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let set = new Set([1, 2])

let processor = {
output(value) {
console.log(value)
},
process(dataSet) {
dataSet.forEach(function(value) {
this.output(value)
}, this)
}
}

processor.process(set)
// 1
// 2

Set 与 Array 的互换

1
2
3
4
5
6
7
let set = new Set([1, 2, 3, 3, 3, 4, 5])
console.log(set)
// Set { 1, 2, 3, 4, 5 }

let array = [...set]
console.log(array)
// [ 1, 2, 3, 4, 5 ]

1
2
3
4
5
6
7
8
function removeDuplicates(items) {
return [...new Set(items)]
}

let numbers = [1, 2, 3, 3, 3, 4, 5]
let noDuplicates = removeDuplicates(numbers)
console.log(noDuplicates)
// [ 1, 2, 3, 4, 5 ]

三、ECMAScript 6 中的 Maps

1
2
3
4
5
6
let map = new Map()
map.set("title", "Understanding ECMAScript 6")
map.set("year", 2016)

console.log(map.get("title")) // Understanding ECMAScript 6
console.log(map.get("year")) // 2016

可以使用对象作为 Map 中的键,这些键不会转换为另一种形式,且都是唯一的:

1
2
3
4
5
6
7
8
9
let map = new Map()
let key1 = {}
let key2 = {}

map.set(key1, 5)
map.set(key2, 42)

console.log(map.get(key1)) // 5
console.log(map.get(key2)) // 42

Map 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let map = new Map()
map.set("name", "Nicholas")
map.set("age", 25)

console.log(map.size) // 2
console.log(map.has("name")) // true
console.log(map.get("name")) // Nicholas

map.delete("name")
console.log(map.has("name")) // false
console.log(map.get("name")) // undefined
console.log(map.size) // 1

map.clear()
console.log(map.has("age")) // false
console.log(map.get("age")) // undefined
console.log(map.size) // 0

forEach

1
2
3
4
5
6
7
8
9
10
let map = new Map([["name", "Nicholas"], ["age", 25]])

map.forEach(function(value, key, ownerMap) {
console.log(key + " " + value)
console.log(ownerMap === map)
})
// name Nicholas
// true
// age 25
// true

参考资料

Understanding ECMAScript 6