我刚刚发现了这个特点:

Map: Map对象是简单的键/值映射。

这让我很困惑。常规JavaScript对象是字典,那么Map与字典有什么不同呢?从概念上讲,它们是相同的(根据Stack Overflow的另一个问题)

文档也没有帮助:

Map对象是键/值对的集合,其中键和值都可以是任意的ECMAScript语言值。不同的键值只能出现在Map集合中的一个键/值对中。使用创建Map时选择的比较算法进行区分的不同键值。

Map对象可以按插入顺序迭代其元素。Map对象必须使用哈希表或其他机制来实现,这些机制提供的访问时间平均与集合中元素的数量呈次线性关系。本Map对象规范中使用的数据结构仅用于描述Map对象所需的可观察语义。它并不是一个可行的实现模型。

听起来还是像个物件,显然我错过了什么。

为什么JavaScript获得一个(受良好支持的)Map对象?它能做什么?


当前回答

除了其他答案之外,我还发现map比对象操作起来更笨拙和冗长。

obj[key] += x
// vs.
map.set(map.get(key) + x)

这一点很重要,因为较短的代码读起来更快,表达更直接,并且更好地记在程序员的脑海里。

另一方面:因为set()返回的是映射,而不是值,所以不可能链式赋值。

foo = obj[key] = x;  // Does what you expect
foo = map.set(key, x)  // foo !== x; foo === map

调试地图也更加痛苦。下图中,你实际上看不到地图中的键是什么。你必须编写代码才能做到这一点。

对象可以被任何IDE计算:

其他回答

TL;DR:你不能在对象中使用键' length'来存储值,但在Map中可以。

对于我来说,不使用object而使用Map的实际考虑是,从实际的角度来看,object的键不是字符串或数字,而是字符串或数字的子集。具体来说,使用与原型的属性名或众所周知的属性冲突的键可能会导致问题。例如,存储具有键长度的值可能会使使用length键来确定给定对象是否为数组的代码混淆。

对象的行为类似于字典,因为JavaScript是动态类型的,允许您随时添加或删除属性。

但是Map()更好,因为它:

提供get、set、has和delete方法。 接受任何类型的键,而不仅仅是字符串。 提供一个易于for-of使用的迭代器,并维护结果的顺序。 在迭代或复制过程中不会出现原型和其他属性的边缘情况。 支持数百万项。 非常快。

如果需要字典,则使用Map()。

但是,如果您只使用基于字符串的键,并且需要最大的读取性能,那么对象可能是更好的选择。这是因为JavaScript引擎在后台将对象编译为c++类,并且属性的访问路径比Map().get()的函数调用要快得多。

这些类也被缓存,所以创建一个具有完全相同属性的新对象意味着引擎将重用一个现有的后台类。添加或删除属性会导致类的形状改变,并重新编译支持类,这就是为什么将一个对象用作添加和删除大量内容的字典会非常慢,但是在不更改对象的情况下读取现有键会非常快。

因此,如果您有一个写一次读的繁重的工作负载和字符串键,那么您可以使用对象作为高性能的字典,但对于其他所有事情使用Map()。

简介:

Object: A data structure in which data is stored as key value pairs. In an object the key has to be a number, string, or symbol. The value can be anything so also other objects, functions, etc. An object is a nonordered data structure, i.e. the sequence of insertion of key value pairs is not remembered ES6 Map: A data structure in which data is stored as key value pairs. In which a unique key maps to a value. Both the key and the value can be in any data type. A map is an iterable data structure. This means that the sequence of insertion is remembered and that we can access the elements in e.g. a for..of loop.

关键的不同点:

Map是有序的且可迭代的,而object不是有序的且不可迭代的(也就是说它们没有[符号]。迭代器]属性。但是,你可以在语法中使用for..遍历键。) 我们可以将任何类型的数据作为Map键,而对象只能将数字、字符串或符号作为键。 Map继承自Map.prototype。这提供了各种实用函数和属性,使Map对象的工作更加容易。

例子:

对象:

让obj = {}; //添加属性到对象 obj。Prop1 = 1; Obj [2] = 2; //获取对象属性的nr console.log(种(obj) . length) //删除属性 删除obj [2] console.log (obj)

Map:

const myMap = new Map(); const keyString = 'a string', keyObj = {}, keyFunc = function() {}; // setting the values myMap.set(keyString, "value associated with 'a string'"); myMap.set(keyObj, 'value associated with keyObj'); myMap.set(keyFunc, 'value associated with keyFunc'); console.log(myMap.size); // 3 // getting the values console.log(myMap.get(keyString)); // "value associated with 'a string'" console.log(myMap.get(keyObj)); // "value associated with keyObj" console.log(myMap.get(keyFunc)); // "value associated with keyFunc" console.log(myMap.get('a string')); // "value associated with 'a string'" // because keyString === 'a string' console.log(myMap.get({})); // undefined, because keyObj !== {} console.log(myMap.get(function() {})) // undefined, because keyFunc !== function () {}

来源:中数

什么时候使用映射而不是简单的JavaScript对象

纯JavaScript对象{key: 'value'}保存结构化数据。但是普通的JavaScript对象有其局限性:

Only strings and symbols can be used as keys of Objects. If we use any other things, say, numbers as keys of an object then during accessing those keys we will see those keys will be converted into strings implicitly causing us to lose consistency of types. const names= {1: 'one', 2: 'two'}; Object.keys(names); // ['1', '2'] There are chances of accidentally overwriting inherited properties from prototypes by writing JavaScript identifiers as key names of an object (e.g., toString, constructor, etc.) Another object cannot be used as key of an object, so no extra information can be written for an object by writing that object as key of another object and value of that another object will contain the extra information Objects are not iterators The size of an object cannot be determined directly

对象的这些局限性可以通过映射来解决,但我们必须将映射视为对象的补充而不是替代。基本上Map只是数组的数组,但我们必须将该数组作为参数传递给Map对象,并使用new关键字,否则仅对于数组数组,Map的有用属性和方法不可用。记住数组中的数组或Map中的键值对必须用逗号分隔,不能像普通对象那样用冒号分隔。

决定使用Map还是Object的三个技巧

当键在运行时之前是未知的时,使用map而不是对象,因为由用户输入形成的键或在不知情的情况下,如果这些键覆盖了对象的继承属性,则会破坏使用对象的代码,因此map在这种情况下更安全。当所有键都是相同类型且所有映射都是相同类型时,也要使用映射。 如果需要将基本值存储为键,则使用映射。 如果需要对单个元素进行操作,则使用对象。

使用地图的好处

1. Map接受任何键类型,并保留键类型:

我们知道,如果对象的键不是字符串或符号,那么JavaScript会隐式地将其转换为字符串。相反,Map接受任何类型的键:字符串、数字、布尔值、符号。等,Map保留原来的键类型。在这里,我们将在Map中使用number作为键,它将保持为数字:

    const numbersMap= new Map();
    numbersMap.set(1, 'one');
    numbersMap.set(2, 'two');
    const keysOfMap= [...numbersMap.keys()];

    console.log(keysOfMap);                        // [1, 2]

在Map中,我们甚至可以使用整个对象作为键。有时,我们希望存储一些与对象相关的数据,而不将这些数据附加到对象本身中,以便我们可以使用精简对象,但希望存储关于对象的一些信息。在这种情况下,我们需要使用Map,这样我们就可以将Object作为键,并将对象的相关数据作为值。

    const foo= {name: foo};
    const bar= {name: bar};
    const kindOfMap= [[foo, 'Foo related data'], [bar, 'Bar related data']];

但是这种方法的缺点是通过键访问值的复杂性,因为我们必须遍历整个数组才能获得所需的值。

    function getBy Key(kindOfMap, key) {
        for (const [k, v]  of kindOfMap) {
            if(key === k) {
                return v;
            }
        }
        return undefined;
    }
    getByKey(kindOfMap, foo);            // 'Foo related data'

我们可以通过使用适当的Map来解决不能直接访问值的问题。

    const foo= {name: 'foo'};
    const bar= {name: 'bar'};
    const myMap= new Map();
    myMap.set(foo, 'Foo related data');
    myMap.set(bar, 'Bar related data');

    console.log(myMap.get(foo));            // 'Foo related data'

我们本可以用WeakMap来实现,只需要写入const myMap= new WeakMap()。Map和WeakMap之间的区别在于,WeakMap允许键(这里是对象)的垃圾收集,因此它可以防止内存泄漏,WeakMap只接受对象作为键,并且WeakMap减少了方法集。

2. Map对键名没有限制:

对于普通的JavaScript对象,我们可能会意外地覆盖从原型继承的属性,这可能是危险的。在这里,我们将重写actor对象的toString()属性:

    const actor= {
        name: 'Harrison Ford',
        toString: 'Actor: Harrison Ford'
    };

现在让我们定义一个函数isPlainObject(),来确定所提供的参数是否是一个普通对象,这个函数使用toString()方法来检查它:

    function isPlainObject(value) {
        return value.toString() === '[object Object]';
    }

    isPlainObject(actor);        // TypeError : value.toString is not a function

    // this is because inside actor object `toString` property is a
    // string instead of inherited method from prototype

Map对键名没有任何限制。虽然actorMap对象有一个名为toString的属性,但我们可以使用toString()方法继承自actorMap对象的原型,工作完美。

    const actorMap= new Map();
    actorMap.set('name', 'Harrison Ford');
    actorMap.set('toString', 'Actor: Harrison Ford');
    function isMap(value) {
      return value.toString() === '[object Map]';
    }

    console.log(isMap(actorMap));     // true

如果我们遇到用户输入创建键的情况,那么我们必须在Map中而不是普通对象中获取这些键。这是因为用户可以选择自定义字段名,如toString、构造函数等,那么在普通对象中这样的键名可能会破坏以后使用该对象的代码。所以正确的解决方案是将用户界面状态绑定到map上,没有办法破坏map:

    const userCustomFieldsMap= new Map([['color', 'blue'], 
            ['size', 'medium'], ['toString', 'A blue box']]);

3.Map是可迭代的:

要迭代一个普通对象的属性,我们需要object .entries()或object .keys()。object .entries(plainObject)返回一个从对象中提取的键值对数组,然后我们可以解构这些键和值,并得到正常的键和值输出。

    const colorHex= {
      'white': '#FFFFFF',
      'black': '#000000'
    }

    for(const [color, hex] of Object.entries(colorHex)) {
      console.log(color, hex);
    }
    //
    'white' '#FFFFFF'
    'black' '#000000'

由于Map是可迭代的,这就是为什么我们不需要entries()方法来遍历Map和析构键,值数组可以直接在Map上完成,因为在Map中每个元素都是由逗号分隔的键值对数组。

    const colorHexMap = new Map();
    colorHexMap.set('white', '#FFFFFF');
    colorHexMap.set('black', '#000000');

    for(const [color, hex] of colorHexMap) {
      console.log(color, hex);
    }
    //'white' '#FFFFFF'   'black' '#000000'

map.keys()返回键的迭代器,map.values()返回值的迭代器。

4. 我们很容易知道地图的大小

我们不能直接确定一个普通对象中属性的数量。我们需要一个helper函数,比如object. keys(),它返回一个包含对象键的数组,然后使用length属性,我们可以获得键的数量或普通对象的大小。

    const exams= {'John Rambo': '80%', 'James Bond': '60%'};
    const sizeOfObj= Object.keys(exams).length;
    console.log(sizeOfObj);       // 2

但在地图的情况下,我们可以直接访问地图的大小使用地图。大小属性。

    const examsMap = new Map([['John Rambo', '80%'], ['James Bond', '60%']]);

    console.log(examsMap.size);

根据MDN:

Map对象可以按插入顺序迭代其元素——for..of循环每次迭代将返回一个[key, value]数组。

and

Objects are similar to Maps in that both let you set keys to values, retrieve those values, delete keys, and detect whether something is stored at a key. Because of this, Objects have been used as Maps historically; however, there are important differences between Objects and Maps that make using a Map better. An Object has a prototype, so there are default keys in the map. However, this can be bypassed using map = Object.create(null). The keys of an Object are Strings, where they can be any value for a Map. You can get the size of a Map easily while you have to manually keep track of size for an Object.

Map

按顺序迭代是开发人员一直想要的功能,部分原因是它可以确保在所有浏览器中都具有相同的性能。所以对我来说,这是一个大问题。

myMap.has(key)方法将特别方便,还有myMap.has(key)方法。大小属性。