JavaScript中的映射与对象

JavaScript中的映射与对象,javascript,dictionary,ecmascript-6,Javascript,Dictionary,Ecmascript 6,我刚刚发现: 映射:映射对象是简单的键/值映射 这让我很困惑。常规JavaScript对象是字典,那么它与字典有何不同呢?从概念上讲,它们是相同的(根据) 文档也没有帮助: 映射对象是键/值对的集合,其中键和值都可以是任意ECMAScript语言值。不同的键值只能出现在映射集合中的一个键值对中。使用创建地图时选择的比较算法来区分不同的键值 贴图对象可以按插入顺序迭代其元素。映射对象必须使用哈希表或其他机制实现,这些机制平均提供的访问时间与集合中的元素数呈次线性关系。本地图对象规范中使用的数据结构

我刚刚发现:

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

这让我很困惑。常规JavaScript对象是字典,那么它与字典有何不同呢?从概念上讲,它们是相同的(根据)

文档也没有帮助:

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

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

…对我来说仍然像个目标,所以很明显我错过了什么

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

根据mozilla的说法:

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

对象与贴图类似,都可以将关键点设置为值, 检索这些值,删除键,并检测是否存在错误 储存在钥匙处。因此,对象已被用作贴图 历史上;但是,对象之间存在重要的差异 以及使使用地图变得更好的地图

对象有一个原型,因此地图中有默认关键点。 但是,可以使用map=Object.create(null)绕过这一点。这个 对象的键是字符串,它们可以是贴图的任何值。 您可以很容易地获得地图的大小,而您必须手动保留 跟踪对象的大小

当关键点在运行时之前未知时,以及 所有键都是相同的类型,所有值都是相同的类型

当存在对单个元素进行操作的逻辑时,使用对象

有序的可写性是开发人员长期以来一直想要的特性,部分原因是它确保了所有浏览器的性能相同。所以对我来说这是一个大问题


该方法特别方便,属性也非常方便。

除了可以按照定义良好的顺序进行编辑,并且能够使用任意值作为键(除了
-0
),映射还可以使用,原因如下:

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

console.log(examsMap.size);
  • 规范强制映射操作平均为次线性

    任何非愚蠢的对象实现都将使用哈希表或类似的方法,所以属性查找平均来说可能是常量。然后,对象可能比贴图更快。但这不是规范所要求的

  • 对象可能有令人讨厌的意外行为

    例如,假设您没有将任何
    foo
    属性设置为新创建的对象
    obj
    ,因此您希望
    obj.foo
    返回未定义。但是
    foo
    可以是从
    Object.prototype
    继承的内置属性。或者您尝试使用赋值创建
    obj.foo
    ,但是
    Object.prototype
    中的某些setter会运行,而不是存储您的值

    地图阻止了这类事情。好吧,除非某些脚本弄乱了
    Map.prototype
    。和
    Object.create(null)
    也可以工作,但是这样就失去了简单的对象初始值设定项语法


这两个提示可以帮助您决定是使用地图还是对象:

  • 当关键点在运行时之前未知时,以及 所有键都是相同的类型,所有值都是相同的类型

  • 如果需要将基元值存储为键,请使用映射 因为对象将每个键视为一个字符串或其数值, 布尔值或任何其他基本值

  • 当存在对单个元素进行操作的逻辑时,使用对象

资料来源:

关键区别在于对象仅支持字符串和符号键,而as映射或多或少支持任何键类型

如果我执行
obj[123]=true
,然后执行
Object.keys(obj)
,那么我将得到
[“123”]
,而不是
[123]
。映射将保留键的类型并返回
[123]
,这很好。贴图还允许您将对象用作关键点。传统上,要做到这一点,您必须为对象提供某种唯一标识符来散列它们(我想我从来没有在JS中看到过像
getObjectId
这样的标准)。地图也保证了秩序的保存,所以在保存方面,地图更适合,有时还可以省去你需要做一些排序的工作

在实践中,地图和物体之间有几个优点和缺点。对象紧密地集成到JavaScript的核心中既有优点也有缺点,这使得它们在关键支持方面的差异大大超出了它们的范围

一个直接的优势是,您对对象有语法支持,从而可以轻松访问元素。您还可以使用JSON直接支持它。当用作散列时,获取一个没有任何属性的对象是很烦人的。默认情况下,如果要将对象用作哈希表,它们将受到污染,并且在访问属性时,您通常必须对其调用
hasOwnProperty
。您可以在这里看到默认情况下对象是如何受到污染的,以及如何创建希望未受污染的对象以用作哈希:

({}).toString
    toString() { [native code] }
JSON.parse('{}').toString
    toString() { [native code] }
(Object.create(null)).toString
    undefined
JSON.parse('{}', (k,v) => (typeof v === 'object' && Object.setPrototypeOf(v, null) ,v)).toString
    undefined
对对象的污染不仅会使代码变得更烦人、更慢等,还会对安全性产生潜在影响

对象不是纯粹的哈希表,但正在尝试
JSON.parse(str, (k,v) => {
    if(typeof v !== 'object') return v;
    let m = new Map();
    for(k in v) m.set(k, v[k]);
    return m;
});
// An alternative to this it to use a replacer in JSON.stringify.
Map.prototype.toJSON = function() {
    return JSON.stringify({
        keys: Array.from(this.keys()),
        values: Array.from(this.values())
    });
};
Object Set Took: 146
Object Update Took: 7
Object Get Took: 4
Object Delete Took: 8239
Map Set Took: 80
Map Update Took: 51
Map Get Took: 40
Map Delete Took: 2
Object Set Took: 435
Object Update Took: 126
Object Get Took: 50
Object Delete Took: 2
Map Set Took: 63
Map Update Took: 59
Map Get Took: 33
Map Delete Took: 1
Map Create: 69    // new Map
Object Create: 34 // {}
Chrome Object Took: 61
Chrome Map Took: 67
Firefox Object Took: 54
Firefox Map Took: 139
obj[key] += x
// vs.
map.set(map.get(key) + x)
foo = obj[key] = x;  // Does what you expect
foo = map.set(key, x)  // foo !== x; foo === map
var m = new Map();
var i = 0;
while(1) {
    m.set(((10**30)*Math.random()).toString(36), ((10**30)*Math.random()).toString(36));
    i++;
    if(i%1000 === 0) { console.log(i/1000,"thousand") }
}
// versus:
var m = {};
var i = 0;
while(1) {
    m[((10**30)*Math.random()).toString(36)] = ((10**30)*Math.random()).toString(36);
    i++;
    if(i%1000 === 0) { console.log(i/1000,"thousand") }
}
const numbersMap= new Map();

numbersMap.set(1, 'one');

numbersMap.set(2, 'two');

const keysOfMap= [...numbersMap.keys()];

console.log(keysOfMap);                        // [1, 2]
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'
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'
const actor= {
    name: 'Harrison Ford',
    toString: 'Actor: Harrison Ford'
};
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
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
const userCustomFieldsMap= new Map([['color', 'blue'], ['size', 'medium'], ['toString', 'A blue box']]);
const colorHex= {
  'white': '#FFFFFF',
  'black': '#000000'
}

for(const [color, hex] of Object.entries(colorHex)) {
  console.log(color, hex);
}
//
'white' '#FFFFFF'   
'black' '#000000'
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'
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);