Heim  >  Artikel  >  Web-Frontend  >  Eine kurze Diskussion darüber, wie man Map anstelle von reinen JS-Objekten verwendet

Eine kurze Diskussion darüber, wie man Map anstelle von reinen JS-Objekten verwendet

青灯夜游
青灯夜游nach vorne
2020-12-02 18:06:464902Durchsuche

Eine kurze Diskussion darüber, wie man Map anstelle von reinen JS-Objekten verwendet

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 对象如何解决许多普通对象的问题,包括将键转换为字符串。

1. map 可接受任意类型的键

如上所述,如果对象的键不是字符串或符号,则 JavaScript 会将其隐式转换为字符串。

幸运的是,map 在键类型上不存在问题:

const numbersMap = new Map();

numbersMap.set(1, 'one');
numbersMap.set(2, 'two');

[...numbersMap.keys()]; // => [1, 2]

12numbersMap 中的键。这些键的类型 number 保持不变。

你可以在 map 中使用任何键类型:数字,布尔以及经典的字符串和符号。

const booleansMap = new Map();

booleansMap.set(true, "Yep");
booleansMap.set(false, "Nope");

[...booleansMap.keys()]; // => [true, false]

booleansMap 用布尔值作为键没有问题。

同样,布尔键在普通对象中不起作用。

让我们超越界限:你能把整个对象用作 map 中的键吗?当然可以!

1.1 把对象做为键

假设你需要存储一些与对象相关的数据,但是不把这些数据附加到对象本身。

不能用普通对象这样做。

一种解决方法是用一组对象值元组:

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'

WeakMapMap 的专用版本)你无需为此烦恼。它接受把对象作为键。

MapWeakMap 之间的主要区别是后者允许对作为键的对象进行垃圾回收,从而防止内存泄漏。

把上面的代码重构为使用 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 仅接受把对象作为键,并具有精简的方法集

2. map 对键名没有限制

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

检查普通对象从原型继承的属性和方法列表。要避免使用这些名称定义自定义属性。

例如,假设有一个管理某些自定义字段的用户界面。用户可以通过指定名称和值来添加字段:

Eine kurze Diskussion darüber, wie man Map anstelle von reinen JS-Objekten verwendet

将自定义字段的状态存储到一个普通对象中会很方便:

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() 都能正常工作。

3. map 是可迭代的

为了遍历普通对象的属性,你必须用其他辅助静态函数,例如 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()

Aber eines finde ich sehr ärgerlich: Die Schlüssel der Objekte müssen Strings (oder selten verwendete Symbole) sein. 🎜🎜Was passiert, wenn Zahlen als Schlüssel verwendet werden? In diesem Fall liegt kein Fehler vor: 🎜
const exams = {
  'John Smith': '10 points',
  'Jane Doe': '8 points',
};

Object.keys(exams).length; // => 2
🎜JavaScript wandelt die Schlüssel des Objekts nur implizit in Zeichenfolgen um. Das ist eine knifflige Sache, weil dadurch die Typkonsistenz verloren geht. 🎜🎜In diesem Artikel werde ich das in ES2015 bereitgestellte JavaScript vorstellen Map-Objekte viele der Probleme gewöhnlicher Objekte lösen, einschließlich der Konvertierung von Schlüsseln in Zeichenfolgen. 🎜

1. Map akzeptiert Schlüssel aller Art

🎜Wenn der Schlüssel des Objekts keine Zeichenfolge oder kein Symbol ist, konvertiert JavaScript ihn implizit in eine Zeichenfolge. 🎜🎜Glücklicherweise hat Map kein Problem mit Schlüsseltypen: 🎜
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! 🎜

1.1 Objekte als Schlüssel verwenden

🎜Angenommen, Sie müssen einige Daten im Zusammenhang mit dem Objekt speichern, die Daten jedoch nicht an das Objekt selbst anhängen. 🎜🎜Das ist mit normalen Objekten nicht möglich. 🎜🎜Eine Lösung besteht darin, ein Tupel von Objektwerten zu verwenden: 🎜rrreee🎜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>. 🎜

2. Map hat keine Einschränkungen hinsichtlich der Schlüsselnamen

🎜Jedes Objekt in JavaScript erbt Eigenschaften von seinem Prototypobjekt. Das Gleiche gilt für reguläre JavaScript-Objekte. 🎜🎜Wenn Sie von einem Prototyp geerbte Eigenschaften überschreiben, können Sie Code beschädigen, der von diesen Prototypeigenschaften abhängt: 🎜rrreee🎜Eine Eigenschaft 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: 🎜🎜Eine kurze Diskussion darüber, wie man Map anstelle von reinen JS-Objekten verwendet🎜🎜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 . 🎜

3. Karte ist iterierbar

🎜Um die Eigenschaften gewöhnlicher Objekte zu durchlaufen, müssen Sie andere statische Hilfsfunktionen verwenden, wie z. B. 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. 🎜

4. map的大小

普通对象的另一个问题是你无法轻松确定其拥有的属性数量:

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

5.结论

普通的 JavaScript 对象通常可以很好地保存结构化数据。但是它们有一些限制:

  1. 只能用字符串或符号用作键
  2. 自己的对象属性可能会与从原型继承的属性键冲突(例如,toStringconstructor 等)。
  3. 对象不能用作键

所有这些问题都可以通过 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!

Stellungnahme:
Dieser Artikel ist reproduziert unter:segmentfault.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen