JavaScript-Gewöhnliche Objekte {key: 'value'
können zum Speichern strukturierter Daten verwendet werden. {key: 'value'}
可用于保存结构化数据。
但是我发现很烦人的一件事:对象的键必须是字符串(或很少使用的符号)。
如果用数字作键会怎样?在这种情况下没有错误:
const names = { 1: 'One', 2: 'Two', }; Object.keys(names); // => ['1', '2']
JavaScript 只是将对象的键隐式转换为字符串。这是一件棘手的事,因为你失去了类型的一致性。
在本文中,我将介绍 ES2015 中提供的 JavaScript Map 对象如何解决许多普通对象的问题,包括将键转换为字符串。
如上所述,如果对象的键不是字符串或符号,则 JavaScript 会将其隐式转换为字符串。
幸运的是,map 在键类型上不存在问题:
const numbersMap = new Map(); numbersMap.set(1, 'one'); numbersMap.set(2, 'two'); [...numbersMap.keys()]; // => [1, 2]
1
和 2
是 numbersMap
中的键。这些键的类型 number 保持不变。
你可以在 map 中使用任何键类型:数字,布尔以及经典的字符串和符号。
const booleansMap = new Map(); booleansMap.set(true, "Yep"); booleansMap.set(false, "Nope"); [...booleansMap.keys()]; // => [true, false]
booleansMap
用布尔值作为键没有问题。
同样,布尔键在普通对象中不起作用。
让我们超越界限:你能把整个对象用作 map 中的键吗?当然可以!
假设你需要存储一些与对象相关的数据,但是不把这些数据附加到对象本身。
不能用普通对象这样做。
一种解决方法是用一组对象值元组:
const foo = { name: 'foo' }; const bar = { name: 'bar' }; const kindOfMap = [ [foo, 'Foo related data'], [bar, 'Bar related data'], ];
kindOfMap
是一个包含一对对象和关联值的数组。
这种方法的最大问题是通过键访问值的时间复杂度为 O(n) 。你必须遍历整个数组才能通过键获得所需的值:
function getByKey(kindOfMap, key) { for (const [k, v] of kindOfMap) { if (key === k) { return v; } } return undefined; } getByKey(kindOfMap, foo); // => 'Foo related data'
用 WeakMap(Map
的专用版本)你无需为此烦恼。它接受把对象作为键。
Map
和 WeakMap
之间的主要区别是后者允许对作为键的对象进行垃圾回收,从而防止内存泄漏。
把上面的代码重构为使用 WeakMap
的代码付出的代价微不足道:
const foo = { name: 'foo' }; const bar = { name: 'bar' }; const mapOfObjects = new WeakMap(); mapOfObjects.set(foo, 'Foo related data'); mapOfObjects.set(bar, 'Bar related data'); mapOfObjects.get(foo); // => 'Foo related data'
与 Map
相对,WeakMap
仅接受把对象作为键,并具有精简的方法集。
JavaScript 中的任何对象都从其原型对象继承属性。普通的 JavaScript 对象也是如此。
如果覆盖从原型继承的属性,则可能会破坏依赖于这些原型属性的代码:
function isPlainObject(value) { return value.toString() === '[object Object]'; } const actor = { name: 'Harrison Ford', toString: 'Actor: Harrison Ford' }; // Does not work! isPlainObject(actor); // TypeError: value.toString is not a function
在对象 actor
上定义的属性 toString
覆盖了从原型继承的 toString()
方法。因为它依赖于 toString()
方法,所以这破坏了 isObject()
。
检查普通对象从原型继承的属性和方法列表。要避免使用这些名称定义自定义属性。
例如,假设有一个管理某些自定义字段的用户界面。用户可以通过指定名称和值来添加字段:
将自定义字段的状态存储到一个普通对象中会很方便:
const userCustomFields = { 'color': 'blue', 'size': 'medium', 'toString': 'A blue box' };
但是用户可能会选择一个自定义字段名称,例如 toString
(如例中所示), constructor
等,这可能会破坏你的对象。
不要通过接受用户的输入在普通对象上创建键!
map 则没有这个问题。键的名称不受限制:
function isMap(value) { return value.toString() === '[object Map]'; } const actorMap = new Map(); actorMap.set('name', 'Harrison Ford'); actorMap.set('toString', 'Actor: Harrison Ford'); // Works! isMap(actorMap); // => true
不管 actorMap
是否具有名为 toString
的属性,方法 toString()
都能正常工作。
为了遍历普通对象的属性,你必须用其他辅助静态函数,例如 Object.keys()
或 Object.entries()
(在 ES2017 中可用):
const colorsHex = { 'white': '#FFFFFF', 'black': '#000000' }; for (const [color, hex] of Object.entries(colorsHex)) { console.log(color, hex); } // 'white' '#FFFFFF' // 'black' '#000000'
Object.entries(colorsHex)
返回从对象提取的键值对数组。
但是,map 本身是可迭代的:
const colorsHexMap = new Map(); colorsHexMap.set('white', '#FFFFFF'); colorsHexMap.set('black', '#000000'); for (const [color, hex] of colorsHexMap) { console.log(color, hex); } // 'white' '#FFFFFF' // 'black' '#000000'
colorsHexMap
是可迭代的。你可以在任何可迭代的地方使用它:for()
循环,展开运算符 [...map]
等。
map 还提供了返回迭代的其他方法:map.keys()
遍历键,map.values()
const exams = { 'John Smith': '10 points', 'Jane Doe': '8 points', }; Object.keys(exams).length; // => 2
const examsMap = new Map([ ['John Smith', '10 points'], ['Jane Doe', '8 points'], ]); examsMap.size; // => 2
1
und 2
sind Schlüssel in numbersMap
. Der Typ dieser Schlüssel Nummer bleibt unverändert. 🎜🎜Sie können jeden Schlüsseltyp in einer Karte verwenden: Zahlen, boolesche Werte sowie klassische Zeichenfolgen und Symbole. 🎜rrreee🎜booleansMap
hat kein Problem damit, boolesche Werte als Schlüssel zu verwenden. 🎜🎜Auch hier funktionieren boolesche Schlüssel nicht in normalen Objekten. 🎜🎜Lassen Sie uns die Grenzen erweitern: Kann man ein ganzes Objekt als Schlüssel in einer Karte verwenden? Sicher! 🎜kindOfMap
ist ein Array, das ein Paar von Objekten und zugehörigen Werten enthält. 🎜🎜Das größte Problem bei diesem Ansatz besteht darin, dass die zeitliche Komplexität des Zugriffs auf den Wert per Schlüssel O(n) beträgt. Sie müssen über das gesamte Array iterieren, um den gewünschten Wert per Schlüssel zu erhalten: 🎜rrreee🎜Verwenden Sie WeakMap (eine dedizierte Version von Map
) Darüber müssen Sie sich keine Sorgen machen. Es akzeptiert Objekte als Schlüssel. 🎜🎜Der Hauptunterschied zwischen Map
und WeakMap
besteht darin, dass letzteres die Garbage Collection von Objekten ermöglicht, die Schlüssel sind, und so Speicherlecks verhindert. 🎜🎜Die Umgestaltung des obigen Codes zur Verwendung von WeakMap
ist mit geringem Aufwand verbunden: 🎜rrreee🎜Im Gegensatz zu Map
akzeptiert WeakMap
nur Object als Schlüssel und verfügt über einen optimierten Methodensatz a>. 🎜toString
, die für ein Objekt actor
definiert ist, überschreibt die toString()
Methode der prototypischen Vererbung. Da es auf der Methode toString()
basiert, unterbricht dies isObject()
. 🎜🎜 Überprüfen Sie, ob normale Objekte vom Prototyp Liste der Eigenschaften und Methoden. Vermeiden Sie die Verwendung dieser Namen zum Definieren benutzerdefinierter Eigenschaften. 🎜🎜Stellen Sie sich zum Beispiel vor, Sie hätten eine Benutzeroberfläche, die einige benutzerdefinierte Felder verwaltet. Benutzer können Felder hinzufügen, indem sie Namen und Werte angeben: 🎜🎜🎜🎜Es wäre praktisch, den Status des benutzerdefinierten Felds in einem einfachen Objekt zu speichern: 🎜rrreee🎜Der Benutzer kann jedoch einen benutzerdefinierten Feldnamen wählen, z. B. toString
( Wie im Beispiel gezeigt, constructor
usw., kann dies Ihr Objekt zerstören. 🎜🎜Erstellen Sie keine Schlüssel für normale Objekte, indem Sie Eingaben vom Benutzer akzeptieren! 🎜🎜map hat dieses Problem nicht. Der Name des Schlüssels ist nicht eingeschränkt: 🎜rrreee🎜Die Methode toString()
funktioniert unabhängig davon, ob die actorMap
eine Eigenschaft namens toString
hat . 🎜Object.keys()
oder Object.entries()
(verfügbar in ES2017): 🎜rrreee🎜Object.entries(colorsHex)
Gibt ein Array von aus dem Objekt extrahierten Schlüssel-Wert-Paaren zurück. 🎜🎜Allerdings ist die Karte selbst iterierbar: 🎜rrreee🎜colorsHexMap
ist iterierbar. Sie können es überall iterierbar verwenden: for()
Schleifen, Abrolloperatoren [...map]
usw. 🎜🎜map bietet auch andere Methoden, die Iterationen zurückgeben: map.keys()
iteriert über Schlüssel, map.values()
iteriert über Werte. 🎜普通对象的另一个问题是你无法轻松确定其拥有的属性数量:
const exams = { 'John Smith': '10 points', 'Jane Doe': '8 points', }; Object.keys(exams).length; // => 2
要确定 exams
的大小,你必须通过它所有键来确定它们的数量。
map 提供了一种替代方法,通过它的访问器属性 size
计算键值对:
const examsMap = new Map([ ['John Smith', '10 points'], ['Jane Doe', '8 points'], ]); examsMap.size; // => 2
确定 map 的大小更加简单:examsMap.size
。
普通的 JavaScript 对象通常可以很好地保存结构化数据。但是它们有一些限制:
toString
,constructor
等)。所有这些问题都可以通过 map 轻松解决。而且它们提供了诸如迭代器和易于进行大小查找之类的好处。
不要将 map 视为普通对象的替代品,而应视为补充。
你知道 map 相对于普通对象的其他好处吗?请在下面写下你的评论!
原文地址:https://dmitripavlutin.com/maps-vs-plain-objects-javascript/
译文地址:https://segmentfault.com/a/1190000020660481
更多编程相关知识,请访问:编程课程!!
Das obige ist der detaillierte Inhalt vonEine kurze Diskussion darüber, wie man Map anstelle von reinen JS-Objekten verwendet. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!