【问题标题】:How do the ES6 Map shims workES6 Map 垫片是如何工作的
【发布时间】:2016-06-24 14:18:08
【问题描述】:

根据我对文档(here 和 here)的理解,需要引用内存地址才能使其工作:

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 shim 可用:https://github.com/zloirock/core-js#map。我尝试阅读源代码,但它过于简洁抽象(internally 使用 strong collection which then imports 10 more files)

问题

请回答以下任一问题

  • 是否有一个简单的技巧,甚至可以真正做到(无需字符串化)吗?
  • 也许它会改变 foo 以在其上存储一些字符串,然后将其用作键?
  • 还有其他问题,也许我读错了文档?

【问题讨论】:

  • 我很确定垫片只是使用 === 来比较对象身份,因此有许多 O(N) 操作而不是 O(1) 或 O(log(N))本机实现可能具有的操作。
  • ES2015 Map 无法正确填充,我相信最接近的实现是记录键、某种迭代以及与对象的定期比较,正如 Jeremy 所提到的那样。
  • Here's 一个可读的 polyfill 顺便说一句
  • @ArtoAle 变异并在密钥上添加 _id 以用于在接受的答案中进行查找解决方案就是这样做的。尽管如此,我的怀疑是正确的,ES5 没有 零影响 实现。感谢您的所有时间????
  • @basarat 完全同意-您可以通过修改关键对象来提高性能-即使您可能无法这样做,例如如果key 已经通过Object.freeze

标签: javascript typescript ecmascript-6


【解决方案1】:

我想到了两种方法。首先,很明显,您可以拥有一个键数组,并对其进行线性搜索:

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”名称(这可以在 Map 构造函数中轻松设置)。

【讨论】:

    【解决方案2】:

    诀窍是存储在一个数组中,并通过迭代和使用严格比较在 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)];
            }
        };
    }
    

    【讨论】:

      【解决方案3】:

      看看我的 polyfill here。我不是在宣传我的 polyfill,我只想说它是我目前还没有找到的最简单和最直接的,因此它最适合学习和教育分析。基本上,它的工作原理是使用查找表来查找键和对应的值表,如下图所示。

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

      在内部,每个项目都存储在不同的索引中,或者至少我喜欢这样做。这也类似于浏览器内部实现它的方式。不幸的是,我看到其他一些 polyfill 试图将密钥存储在对象本身上,并弄乱了所有内部方法来隐藏它,导致整个网页的运行速度慢了 10000%,而且地图速度非常慢,几乎需要整毫秒只是为了设置和获取新属性。另外,我无法理解他们花了多少时间来尝试修补所有内部方法,例如hasOwnProperty。

      至于我的 polyfill 如何以及为什么工作,javascript 对象存储在内存中的不同位置。这就是为什么 [] !== [] 和 javascript 对象数组上的 indexOf 可以正常工作的原因。这是因为它们不是同一个数组。

      【讨论】:

        猜你喜欢
        • 2014-10-06
        • 2021-12-25
        • 1970-01-01
        • 2018-01-22
        • 1970-01-01
        • 1970-01-01
        • 2015-02-04
        • 2011-01-08
        • 2014-11-04
        相关资源
        最近更新 更多