首頁  >  文章  >  web前端  >  淺談使用map取代純JS物件的方法

淺談使用map取代純JS物件的方法

青灯夜游
青灯夜游轉載
2020-12-02 18:06:464900瀏覽

淺談使用map取代純JS物件的方法

JavaScript 普通物件 {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()

檢查普通物件從原型繼承的屬性和方法清單。若要避免使用這些名稱定義自訂屬性。

例如,假設有一個管理某些自訂欄位的使用者介面。使用者可以透過指定名稱和值來新增欄位:

淺談使用map取代純JS物件的方法

將自訂欄位的狀態儲存到一個普通物件中會很方便:

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() 遍歷值。

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

更多编程相关知识,请访问:编程课程!!

以上是淺談使用map取代純JS物件的方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:segmentfault.com。如有侵權,請聯絡admin@php.cn刪除