Как работают прокладки карт ES6


Исходя из моего понимания документов (здесь и здесь ) для его работы потребуется ссылка на адрес памяти:

const foo = {};
const map = new Map();
map.set(foo,'123');  // Can only be done if memory address of `foo` is known. Any other shimming would require stringification of foo

Это связано с тем, что ключи объекта JavaScript {} могут быть только строками (по крайней мере, в ES5).

Но я вижу, что Map прокладка доступна : https://github.com/zloirock/core-js#map . я попытался прочитать источник, но его слишком аккуратно абстрагировали (внутренне использует сильную коллекцию, которая затем импортирует еще 10 файлы )

Вопрос

Ответьте на любой из следующих вопросов, пожалуйста

  • Есть ли в этом простой трюк, и действительно ли это можно сделать (без стрингификации)?
  • возможно, он мутирует foo, чтобы сохранить на нем какую-то строку, а затем использует ее в качестве ключа?
  • что-то еще, и, возможно, я неправильно читаю документы?
3 12

3 ответа:

Существует два способа, которые приходят на ум. Во-первых, очевидно, что вы можете иметь массив ключей и искать его линейно:

Map1 = {
    keys: [],
    values: [],
};

Map1.set = function(key, val) {
    var k = this.keys.indexOf(key);
    if(k < 0)
        this.keys[k = this.keys.length] = key;
    this.values[k] = val;
};

Map1.get = function(key) {
    return this.values[this.keys.indexOf(key)];
};


foo = {};
bar = {};

Map1.set(foo, 'xxx');
Map1.set(bar, 'yyy');

document.write(Map1.get(foo) + Map1.get(bar) + "<br>")

Второй вариант заключается в добавлении специального маркера "ключ" к объекту, который используется в качестве ключа:

Map2 = {
    uid: 0,
    values: {}
};

Map2.set = function(key, val) {
    key = typeof key === 'object'
        ? (key.__uid = key.__uid || ++this.uid)
        : String(key);
    this.values[key] = val;
};

Map2.get = function(key) {
    key = typeof key === 'object'
        ? key.__uid
        : String(key);
    return this.values[key];
};


foo = {};
bar = {};

Map2.set(foo, 'xxx');
Map2.set(bar, 'yyy');

document.write(Map2.get(foo) + Map2.get(bar) + "<br>")

В отличие от первого варианта, второй вариант-O(1). Это можно сделать более точно, сделав uid недоступным для записи / перечисляемым. Кроме того, каждый Map должен иметь свое собственное имя " uid " (это можно легко настроить на карте конструктор).

Хитрость состоит в том, чтобы хранить в массиве и выполнять поиск за O(n) время, повторяя и используя строгое сравнение-вместо использования истинной хэш-функции, которая была бы O (1) поиском. Например, рассмотрим следующее:

var myObj = {};

var someArray = [{}, {}, myObj, {}];

console.log(someArray.indexOf(myObj)); // returns 2

Вот моя реализация из другого ответа: Javascript HashTable use Object key

function Map() {
    var keys = [], values = [];

    return {
        put: function (key, value) {
            var index = keys.indexOf(key);
            if(index == -1) {
                keys.push(key);
                values.push(value);
            }
            else {
                values[index] = value;
            }
        },
        get: function (key) {
            return values[keys.indexOf(key)];
        }
    };
}

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

var k = {}, j = [], m = document, z = NaN;
var m = new Map([
    [k, "foobar"], [j, -0xf], [m, true], [z, function(){}]
]);




Index      Key                 Value
##### ################    ################
0.    k ({})              "foobar"
1.    j ([])              -15
2.    m (Document)        true
3.    z (NaN)             function(){}

Внутренне каждый элемент хранится в другом индексе, или, по крайней мере, так мне нравится это делать. Это тоже аналогично тому, как браузер реализует его внутренне. К сожалению, я видел некоторые другие полифайлы, которые пытаются вместо этого сохранить ключ на самом объекте и запутать все внутренние методы, чтобы скрыть его, в результате чего вся веб-страница работает на 10000% медленнее, а карты настолько медленны, что требуется почти полная миллисекунда, чтобы просто установить и получить новые свойства. Кроме того, я не могу понять, сколько бесчисленных часов они простояли, просто пытаясь исправить все внутренние методы, такие как: hasOwnProperty.

Что касается того, как и почему работает my polyfill, объекты javascript хранятся в другом месте в памяти. Именно поэтому [] !== [] и indexOf на массиве объектов javascript работают правильно. Это потому, что они не являются одним и тем же массивом.