手写实现 Set 集合,深入理解其核心原理
2022-09-15 03:35:27
Set 集合
从使用方式来看,const set = new Set([1, 2, 3]) 说明 Set 本身是一个构造函数,接受一个数组作为初始值。
除了数组外,还可以传入其他可迭代对象
const set = new Set(new Set([1, 2, 3, 4]))
Set 的具体属性方法就不在这里罗列了,点这里。
需要关注一下各方法的返回值:
const set = new Set()
// add() 返回 Set 实例本身,支持链式调用
set.add(1).add(2) // -> Set {1, 2}
// delete() 返回布尔值,表示是否删除成功
set.delete(1) // -> true
set.delete(99) // -> false
// has() 返回布尔值,表示元素是否存在
set.has(2) // -> true
set.has(99) // -> false
/**
* 迭代器方法
*/
// keys() 返回键的迭代器
set.keys() // -> SetIterator [2]
// values() 返回值的迭代器
set.values() // -> SetIterator [2]
// entries() 返回键值对的迭代器
set.entries() // -> SetIterator [[2, 2]]
// forEach() 没有返回值
set.forEach(value => console.log(value))
实现-步骤 1
Set 的核心特点:
- 元素不重复:相同值只会存储一次
- 接受可迭代对象:可以不传参数,如果传参数则必须是可迭代对象(像数组、Set、字符串等)
结合这些特点,我们能轻松写出基础结构:
class MySet {
/**
* 构造函数
* @param {Iterable} [iterator] - 可迭代对象,用于初始化 Set
* @throws {TypeError} 当传入的参数不是可迭代对象时抛出
*/
constructor(iterator = []) {
// 检查是否为可迭代对象
if (typeof iterator[Symbol.iterator] !== 'function') {
throw new TypeError('iterator is not iterable')
}
// 使用数组存储数据
this._datas = []
// 遍历可迭代对象,将每个元素添加到 Set 中
for (const item of iterator) {
this.add(item)
}
}
add(value) {
if (!this.has(value)) {
this._datas.push(value)
}
return this
}
/**
* 检查元素是否存在
* @param {*} value - 要检查的值
* @returns {boolean} 元素存在返回 true,否则返回 false
*/
has(value) {
return this._datas.some((data) => {
// 处理 +0 和 -0 的情况
if (data === 0 && value === 0) {
return true
}
return Object.is(data, value)
})
}
/**
* 判断两个值是否相等
* @param {*} value1 - 第一个值
* @param {*} value2 - 第二个值
* @returns {boolean} 相等返回 true,否则返回 false
*/
isEquals(value1, value2) {
// 处理 +0 和 -0 的情况,两者视为相等
if (value1 === 0 && value2 === 0) {
return true
}
return Object.is(value1, value2)
}
delete(value) {
const originLength = this._datas.length
// 过滤掉要删除的元素
this._datas = this._datas.filter(item => !this.isEquals(item, value))
// 如果长度变化,说明删除成功
return this._datas.length !== originLength
}
get size() {
return this._datas.length
}
clear() {
this._datas = []
}
}
实现-步骤 2
在实现了基础结构和方法后,还需要实现迭代器相关的方法。这些方法都返回一个迭代器对象,支持 for...of 循环。
class MySet {
// ... 之前的代码
keys() {
return this._createIterator()
}
values() {
return this._createIterator()
}
entries() {
const value = this._datas
const iterator = function* () {
for (const val of value) {
yield [val, val]
}
}
const it = iterator()
it[Symbol.iterator] = it
return it
}
forEach(callbackFn, thisArg) {
const value = this._datas
for (const val of value) {
callbackFn.call(thisArg, val, val, this)
}
}
/**
* 创建迭代器
* @returns {Iterator} 返回迭代器对象
* @private
*/
_createIterator() {
const value = this._datas
// 使用生成器函数创建迭代器
const iterator = function* () {
for (const val of value) {
yield val
}
}
const it = iterator()
// 设置迭代器自身为迭代器方法,支持可迭代协议
it[Symbol.iterator] = function () {
return it
}
return it
}
}
Set 是这样的, Map 和这个也差不多。 等待有缘人给补一下 😁