Моделирование серии ES6 реализует структуру данных Set.

внешний интерфейс GitHub Promise QUnit
Моделирование серии ES6 реализует структуру данных Set.

основное введение

ES6 предоставляет новую структуру данных Set.

Он похож на массив, но все значения членов уникальны и нет повторяющихся значений.

инициализация

Сам Set является конструктором, который генерирует структуру данных Set.

let set = new Set();

Функция Set может принимать массив (или другую структуру данных с итерируемым интерфейсом) в качестве параметра для инициализации.

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

set = new Set(document.querySelectorAll('div'));
console.log(set.size); // 66

set = new Set(new Set([1, 2, 3, 4]));
console.log(set.size); // 4

свойства и методы

Методы операции:

  1. add(value): добавить значение и вернуть саму структуру Set.
  2. удалить (значение): удалить значение и вернуть логическое значение, указывающее, было ли удаление успешным.
  3. has(value): возвращает логическое значение, указывающее, является ли значение членом набора.
  4. clear(): очистить все члены, без возвращаемого значения.

Например:

let set = new Set();
console.log(set.add(1).add(2)); // Set [ 1, 2 ]

console.log(set.delete(2)); // true
console.log(set.has(2)); // false

console.log(set.clear()); // undefined
console.log(set.has(1)); // false

Причина, по которой каждая операция является консольной, состоит в том, чтобы все обращали внимание на возвращаемое значение каждой операции.

Методы обхода следующие:

  1. keys(): возвращает обходчик имен ключей
  2. values(): итератор, который возвращает ключевые значения
  3. entry(): возвращает обход пар ключ-значение
  4. forEach(): используйте функцию обратного вызова для обхода каждого члена, без возвращаемого значения.

Обратите внимание, что keys(), values(), entry() возвращают обходчики

let set = new Set(['a', 'b', 'c']);
console.log(set.keys()); // SetIterator {"a", "b", "c"}
console.log([...set.keys()]); // ["a", "b", "c"]
let set = new Set(['a', 'b', 'c']);
console.log(set.values()); // SetIterator {"a", "b", "c"}
console.log([...set.values()]); // ["a", "b", "c"]
let set = new Set(['a', 'b', 'c']);
console.log(set.entries()); // SetIterator {"a", "b", "c"}
console.log([...set.entries()]); // [["a", "a"], ["b", "b"], ["c", "c"]]
let set = new Set([1, 2, 3]);
set.forEach((value, key) => console.log(key + ': ' + value));
// 1: 1
// 2: 2
// 3: 3

Атрибуты:

  1. Set.prototype.constructor: конструктор, по умолчанию используется функция Set.
  2. Set.prototype.size: возвращает общее количество членов экземпляра Set.

Реализация моделирования, первое издание

Если вы хотите смоделировать и реализовать простую структуру данных Set и реализовать методы add, delete, has, clear и forEach, написать ее по-прежнему очень просто.Вот непосредственно код:

/**
 * 模拟实现第一版
 */
(function(global) {

    function Set(data) {
        this._values = [];
        this.size = 0;

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

    Set.prototype['add'] = function(value) {
        if (this._values.indexOf(value) == -1) {
            this._values.push(value);
            ++this.size;
        }
        return this;
    }

    Set.prototype['has'] = function(value) {
        return (this._values.indexOf(value) !== -1);
    }

    Set.prototype['delete'] = function(value) {
        var idx = this._values.indexOf(value);
        if (idx == -1) return false;
        this._values.splice(idx, 1);
        --this.size;
        return true;
    }

    Set.prototype['clear'] = function(value) {
        this._values = [];
        this.size = 0;
    }

    Set.prototype['forEach'] = function(callbackFn, thisArg) {
        thisArg = thisArg || global;
        for (var i = 0; i < this._values.length; i++) {
            callbackFn.call(thisArg, this._values[i], this._values[i], this);
        }
    }

    Set.length = 0;

    global.Set = Set;

})(this)

Мы можем написать кусок тестового кода:

let set = new Set([1, 2, 3, 4, 4]);
console.log(set.size); // 4

set.delete(1);
console.log(set.has(1)); // false

set.clear();
console.log(set.size); // 0

set = new Set([1, 2, 3, 4, 4]);
set.forEach((value, key, set) => {
	console.log(value, key, set.size)
});
// 1 1 4
// 2 2 4
// 3 3 4
// 4 4 4

Реализация моделирования, второе издание

В первой версии мы использовали indexOf, чтобы определить, является ли добавленный элемент дубликатом, и, по сути, мы использовали === для сравнения для NaN, потому что:

console.log([NaN].indexOf(NaN)); // -1

Реализованный макет Set может фактически добавлять несколько NaN без дедупликации, однако для реальной структуры данных Set:

let set = new Set();
set.add(NaN);
set.add(NaN);
console.log(set.size); // 1

Поэтому нам нужно обрабатывать значение NaN отдельно.

Способ справиться с этим состоит в том, чтобы заменить его уникальным значением, когда считается, что добавленное значение равно NaN, например, строка, которую трудно повторить, похожа на@@NaNValue, конечно, когда речь идет об уникальных значениях, мы также можем использовать Symbol напрямую, код выглядит следующим образом:

/**
 * 模拟实现第二版
 */
(function(global) {

    var NaNSymbol = Symbol('NaN');

    var encodeVal = function(value) {
        return value !== value ? NaNSymbol : value;
    }

    var decodeVal = function(value) {
        return (value === NaNSymbol) ? NaN : value;
    }

    function Set(data) {
        this._values = [];
        this.size = 0;

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

    }

    Set.prototype['add'] = function(value) {
        value = encodeVal(value);
        if (this._values.indexOf(value) == -1) {
            this._values.push(value);
            ++this.size;
        }
        return this;
    }

    Set.prototype['has'] = function(value) {
        return (this._values.indexOf(encodeVal(value)) !== -1);
    }

    Set.prototype['delete'] = function(value) {
        var idx = this._values.indexOf(encodeVal(value));
        if (idx == -1) return false;
        this._values.splice(idx, 1);
        --this.size;
        return true;
    }

    Set.prototype['clear'] = function(value) {
        ...
    }

    Set.prototype['forEach'] = function(callbackFn, thisArg) {
        ...
    }

    Set.length = 0;

    global.Set = Set;

})(this)

Напишите тестовый пример:

let set = new Set([1, 2, 3]);

set.add(NaN);
console.log(set.size); // 3

set.add(NaN);
console.log(set.size); // 3

Реализация моделирования, 3-е издание

При моделировании реализации Set больше всего проблем вызывает реализация и обработка итераторов, например, инициализация и выполнение методов keys(), values(), entry() будут возвращать итераторы:

let set = new Set([1, 2, 3]);

console.log([...set]); // [1, 2, 3]
console.log(set.keys()); // SetIterator {1, 2, 3}
console.log([...set.keys()]); // [1, 2, 3]
console.log([...set.values()]); // [1, 2, 3]
console.log([...set.entries()]); // [[1, 1], [2, 2], [3, 3]]

И Set также поддерживает передачу итераторов при инициализации:

let set = new Set(new Set([1, 2, 3]));
console.log(set.size); // 3

При инициализации итератора мы можем"Серия итераторов ES6 и для"Функция forOf, реализованная в моделировании, проходит через интерфейс Symbol.iterator входящего итератора, а затем, в свою очередь, выполняет метод add.

И когда метод keys() выполняется, мы можем вернуть объект, а затем развернуть для него интерфейс Symbol.iterator Реализованный код также является окончательным кодом следующим образом:

/**
 * 模拟实现第三版
 */
(function(global) {

    var NaNSymbol = Symbol('NaN');

    var encodeVal = function(value) {
        return value !== value ? NaNSymbol : value;
    }

    var decodeVal = function(value) {
        return (value === NaNSymbol) ? NaN : value;
    }

    var makeIterator = function(array, iterator) {
        var nextIndex = 0;

        // new Set(new Set()) 会调用这里
        var obj = {
            next: function() {
                return nextIndex < array.length ? { value: iterator(array[nextIndex++]), done: false } : { value: void 0, done: true };
            }
        };

        // [...set.keys()] 会调用这里
        obj[Symbol.iterator] = function() {
            return obj
        }

        return obj
    }

    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();
        }
    }

    function Set(data) {
        this._values = [];
        this.size = 0;

        forOf(data, (item) => {
            this.add(item);
        })

    }

    Set.prototype['add'] = function(value) {
        value = encodeVal(value);
        if (this._values.indexOf(value) == -1) {
            this._values.push(value);
            ++this.size;
        }
        return this;
    }

    Set.prototype['has'] = function(value) {
        return (this._values.indexOf(encodeVal(value)) !== -1);
    }

    Set.prototype['delete'] = function(value) {
        var idx = this._values.indexOf(encodeVal(value));
        if (idx == -1) return false;
        this._values.splice(idx, 1);
        --this.size;
        return true;
    }

    Set.prototype['clear'] = function(value) {
        this._values = [];
        this.size = 0;
    }

    Set.prototype['forEach'] = function(callbackFn, thisArg) {
        thisArg = thisArg || global;
        for (var i = 0; i < this._values.length; i++) {
            callbackFn.call(thisArg, this._values[i], this._values[i], this);
        }
    }

    Set.prototype['values'] = Set.prototype['keys'] = function() {
        return makeIterator(this._values, function(value) { return decodeVal(value); });
    }

    Set.prototype['entries'] = function() {
        return makeIterator(this._values, function(value) { return [decodeVal(value), decodeVal(value)]; });
    }

    Set.prototype[Symbol.iterator] = function(){
        return this.values();
    }

    Set.prototype['forEach'] = function(callbackFn, thisArg) {
        thisArg = thisArg || global;
        var iterator = this.entries();

        forOf(iterator, (item) => {
            callbackFn.call(thisArg, item[1], item[0], this);
        })
    }

    Set.length = 0;

    global.Set = Set;

})(this)

Напишите тестовый код:

let set = new Set(new Set([1, 2, 3]));
console.log(set.size); // 3

console.log([...set.keys()]); // [1, 2, 3]
console.log([...set.values()]); // [1, 2, 3]
console.log([...set.entries()]); // [1, 2, 3]

QUnit

Из вышесказанного мы также можем обнаружить, что всякий раз, когда мы изменяем версию, мы просто пишем новый тестовый код, но после того, как код будет переписан, будет ли действовать предыдущий тестовый код? Вы случайно изменили что-то, что привело к сбою предыдущего тестового кода?

Чтобы решить эту проблему, мы можем ввести QUnit для написания тестовых случаев для простого сценария имитации реализации Set и создать новый HTML-файл:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>Set 的模拟实现</title>
    <link rel="stylesheet" href="qunit-2.4.0.css">
</head>

<body>
    <div id="qunit"></div>
    <div id="qunit-fixture"></div>
    <script src="qunit-2.4.0.js"></script>
    <script src="polyfill-set.js"></script>
    <script src="test.js"></script>
</body>

</html>

Напишите тестовые примеры, поскольку синтаксис относительно прост, давайте посмотрим непосредственно на некоторые написанные примеры:

QUnit.test("unique value", function(assert) {
    const set = new Set([1, 2, 3, 4, 4]);
    assert.deepEqual([...set], [1, 2, 3, 4], "Passed!");
});

QUnit.test("unique value", function(assert) {
    const set = new Set(new Set([1, 2, 3, 4, 4]));
    assert.deepEqual([...set], [1, 2, 3, 4], "Passed!");
});

QUnit.test("NaN", function(assert) {
    const items = new Set([NaN, NaN]);
    assert.ok(items.size == 1, "Passed!");
});

QUnit.test("Object", function(assert) {
    const items = new Set([{}, {}]);
    assert.ok(items.size == 2, "Passed!");
});

QUnit.test("set.keys", function(assert) {
    let set = new Set(['red', 'green', 'blue']);
    assert.deepEqual([...set.keys()], ["red", "green", "blue"], "Passed!");
});


QUnit.test("set.forEach", function(assert) {
    let temp = [];
    let set = new Set([1, 2, 3]);
    set.forEach((value, key) => temp.push(value * 2) )

    assert.deepEqual(temp, [2, 4, 6], "Passed!");
});

Используйте браузер для предварительного просмотра страницы HTML, эффект выглядит следующим образом:

Qunit截图

Полный исходный код полифилла и Qunit находится вGitHub.com/ в настоящее время имеет бриз….

серия ES6

Адрес каталога серии ES6:GitHub.com/ в настоящее время имеет бриз…

Ожидается, что в серии ES6 будет написано около 20 статей, направленных на углубление понимания некоторых точек знаний ES6, с акцентом на область действия на уровне блоков, шаблоны меток, функции стрелок, реализацию моделирования символов, наборов, карт и обещаний, схему загрузки модулей, асинхронность. обработка и т.п. содержание.

Если есть какие-либо ошибки или неточности, пожалуйста, поправьте меня, большое спасибо. Если вам нравится или у вас есть вдохновение, добро пожаловать в звезду, что также является поощрением для автора.