set

手把手教你实现js中的Set类。😜

简介

Set对象是值的集合,你可以按照插入的顺序迭代它的元素。 Set中的元素只会出现一次,即 Set 中的元素是唯一的。

值的相等
1、Set 中的值总是唯一的, 所以需要判断两个值是否全等
2、NaNundefined 都可以被存储在Set 中,NaN之间被视为相同的值(尽管 NaN !== NaN)

使用

Set实例

        所有Set实例继承自 Set.prototype

属性

Set.prototype.constructor
        返回实例的构造函数。默认情况下是Set

Set.prototype.size
        返回Set对象的值的个数。


操作方法

Set.prototype.has(value)
        返回一个布尔值,表示该值在Set中存在与否。

Set.prototype.add(value)
        在Set对象尾部添加一个元素。返回该Set对象。

Set.prototype.delete(value)
        移除Set的中与这个值相等的元素,返回Set.prototype.has(value)在这个操作前会返回的值(即如果该元素存在,返回true,否则返回false)。Set.prototype.has(value)在此后会返回false

Set.prototype.clear()
        移除Set对象内的所有元素。

demo
let mySet = new Set()

mySet.add(0).add('0').add(null).add(undefined) // Set(4) {0, "0", null, undefined}

mySet.has('0') // true
mySet.has(1) // flase
mySet.has(null) // true

mySet.size // 4

mySet.delete(null) // true, null 从set中移除
mySet.has(null) // false

mySet.size // 3
console.log(mySet) // Set(4) {0, "0", undefined}

迭代方法

Set.prototype.keys()
        返回一个新的迭代器对象,该对象包含Set对象中的按插入顺序排列的所有元素的值。

Set.prototype.values()
        返回一个新的迭代器对象,该对象包含Set对象中的按插入顺序排列的所有元素的值。

Set.prototype.entries()
        返回一个新的迭代器对象,该对象包含Set对象中的按插入顺序排列的所有元素的值的[value, value]数组。为了使这个方法和Map对象保持相似, 每个值的键和值相等。

Set.prototype.forEach(callbackFn[, thisArg])
        按照插入顺序,为Set对象中的每一个值调用一次callBackFn。如果提供了thisArg参数,回调中的this会是这个参数。

demo
let mySet = new Set([0, '0', null, undefined])

mySet.keys() // SetIterator {0, "0", null, undefined}

mySet.values() // SetIterator {0, "0", null, undefined}

mySet.entries() // SetIterator {0, "0", null, undefined}

具体实现

在看具体实现前,先看下的工具方法

1、条件进行判断,抛出异常

const assert = (condition, msg) => {
    if (!condition) throw new Error(msg)
}

2、isDef, 过滤掉nullundefined

const isDef = (value) => {
    return value != void 0
}

3、isIterable, 简单判断value是否是迭代器对象.

const isIterable = (value) => {
    return isDef(value) && typeof value[Symbol.iterator] === 'function'
}

4、

function forOf(obj, cb) {
    let iterable, result

    if (typeof obj[Symbol.iterator] !== "function") throw new TypeError(obj + " is not iterable")
    if (typeof cb !== "function") throw new TypeError('cb must be callable')

    iterable = obj[Symbol.iterator]()

    result = iterable.next()
    while (!result.done) {
        cb(result.value)
        result = iterable.next()
    }
}

源码

class Set {
    constructor (iterable) {
        this.value = []

        assert(this instanceof Set, 'Constructor Set requires "new"') // 必须使用news来调用

        data && data.forEach(function(item) {
            this.add(item)
        }, this)
    }

    get size() {
        return this.value.length
    }

    has(value) {
        return this.value.includes(value) // 使用includes方法判断是否包含 value MDN: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/includes
    }

    add(value) {
        !this.has(value) && this.value.push(value) // value不存在则添加进数组
        return this
    }

    delete(value) {
        let result = this.has(value)
        result && this.value.splice(this.value.indexOf(value), 1)
        return result
    }

    clear() {
        this.value = [] // 赋值一个空数组
    }
    // 返回一个迭代对象,该对象中的值是Set中的value
    keys () {
        return new Iterator(this.value)
    }

    values () {
        return this.keys()
    }
    // 返回一个迭代对象,不同keys和values的是其值是[value, value]
    entries () {
        return new Iterator(this.value, (value) => [value, value])
    }
    // 返回一个新的迭代器对象,该对象包含Set对象中的按插入顺序排列的所有元素的值。
    [Symbol.iterator] () {
        return this.values()
    }
}

参考

MDN
ES6 系列之模拟实现一个 Set 数据结构