深く理解する:ES 6の中のSetとMapデータ構造、Mapとその他のデータ構造の相互変換

9784 ワード

本文の内容は主にチェン一峰の「ES 6標準入門」(第3版)から来ている.『ES 6ノートの勉強——仕事でよく使われるES 6文法』は単にSetとMapについて言及しただけで、今日は暇があってこの文章を書いた.「深く理解する:ES 6のSetとMapデータ構造、Mapと他のデータ構造の相互変換」.

ES 6のSet:


ES 6は、新しいデータ構造であるSetを提供する.配列に似ていますが、メンバーの値は一意で、重複する値はありません.Set自体は、Setデータ構造を生成するための構造関数です.ArrayとSetのコントラストはいずれも多値を格納するコンテナであり,両者は互いに変換できるが,使用シーンには違いがある.①ArrayのindexOf法はSetのhas法より効率が悪い②Setは重複値を含まない(この特性で1つの配列に対する重量除去が可能)③Setはdelete法である値を削除し,Arrayはspliceのみを通過する.両者の使い勝手は前者がより優れている④Arrayの多くの新しい方法map,filter,some,everyなどはSetにはない(ただし両者を互いに変換して使用できる)

一、Setインスタンスの操作方法:

let set = new Set();
set.add(1);
set.add("1");
console.log(set.size); // 2

配列を使用してSetを初期化でき、Setコンストラクタはこれらの値を繰り返し使用しないことを保証します.
let set = new Set([1, 2, 3, 4, 5, 5, 5, 5]);
console.log(set.size); // 5

add(value):値を追加し、Set構造自体has(value)を返します.値がSetのメンバーかどうかを示すブール値を返します.
let set = new Set();
set.add(1);
set.add("1");
console.log(set.has(1)); // true
console.log(set.has(6)); // false

delete(value):値を削除し、正常にclear(value)されたかどうかを示すブール値を返します.すべてのメンバーを消去し、値を返しません.
let set = new Set();
set.add(1);
set.add("1");
console.log(set.has(1)); // true
set.delete(1);
console.log(set.has(5)); // false
console.log(set.size); // 1

set.clear();
console.log(set.size); // 0

二、Set遍歴操作


keys():キー名を返す遍歴器values():健値ペアを返す遍歴器entries():キー値ペアを返す遍歴器forEach():各メンバー
let set = new Set(['red', 'green', 'blue']);

for (let item of set.keys()) {
  console.log(item);
}
// red
// green
// blue

for (let item of set.values()) {
  console.log(item);
}
// red
// green
// blue

for (let item of set.entries()) {
  console.log(item);
}
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]

forEach()
let set = new Set([1, 2]);
set.forEach(function(value, key, ownerSet) {
    console.log(key + ": " + value);
});
//   

//1 :1//2: 2

三、ES 6配列の重さ(面接でも配列の重さの問題をよく聞きます)

let arr = [1, 2, 2, 3];
let set = new Set(arr);
let newArr = Array.from(set);
console.log(newArr); // [1, 2, 3]

Setコレクション変換Array配列
let set = new Set([1, 2, 3, 3, 4]);
let arr = Array.from(set)  //  [1,2,3,4]

四、WeakSet


WeakSetの構造はSetと似ており、Setとは2つの違いがあります.1 weakSetのメンバーはオブジェクトのみで、他のタイプの値ではありません.②weakSetオブジェクトは弱い参照です.他のオブジェクトがオブジェクトを参照しなくなった場合、ゴミ回収メカニズムはオブジェクトが占めるメモリを自動的に回収するので、WeakSetは遍歴できません.
WeakSet構造には次の3つの方法がある:WeakSet.prototype.add(value):WeakSetインスタンスに新しいメンバーを追加します.WeakSet.prototype.delete(value):WeakSetインスタンスの指定したメンバーを消去します.WeakSet.prototype.has(value):WeakSetインスタンスに値があるかどうかを示すブール値を返します.WeakSetの1つの用途は、ドキュメントから削除されるとメモリが漏洩する心配がなく、DOMノードを格納することです.

ES 6のMap:


一、ES 6のMapの意味と基本的な使い方


JavaScriptのオブジェクト(Object)は,本質的にキー値ペアの集合(Hash構造)であるが,従来は文字列のみをキーとして用いていた.これはその使用に大きな制限をもたらした.ES 6は、Mapデータ構造を提供する.オブジェクトに似ており、キー値ペアの集合でもありますが、キーの範囲は文字列に限定されず、オブジェクトを含むさまざまなタイプの値がキーとして使用できます.すなわち,Object構造は「文字列−値」の対応を提供し,Map構造は「値−値」の対応を提供し,より完全なHash構造実現である.
const m = new Map();
const o = {p: 'Hello World'};

m.set(o, 'content')
m.get(o) // "content"

m.has(o) // true
m.delete(o) // true
m.has(o) // false

上のコードはMap構造のsetメソッドを用い,オブジェクトoをmのキーとし,getメソッドを用いてこのキーを読み出し,deleteメソッドを用いてこのキーを削除した.インスタンスのプロパティと操作方法§

二、Map構造の例は以下の属性と操作方法がある。


1.size属性size属性は、Map構造のメンバー総数を返します.
const map = new Map();
map.set('foo', true);
map.set('bar', false);
map.size // 2

2.set(key,value)setメソッドキー名keyに対応するキー値をvalueに設定し、Map構造全体を返します.キーに値がすでにある場合は、キー値が更新され、そうでない場合は、キーが新しく生成されます.
const m = new Map();
m.set('edition', 6)        //      
m.set(262, 'standard')     //     
m.set(undefined, 'nah')    //    undefined

setメソッドは現在のMapオブジェクトを返すので,チェーン書き方が可能である.
let map = new Map()
  .set(1, 'a')
  .set(2, 'b')
  .set(3, 'c');

3.get(key)getメソッドはkeyに対応するキー値を読み出し、keyが見つからない場合はundefinedを返します.
const m = new Map();
const hello = function() {console.log('hello');};
m.set(hello, 'Hello ES6!') //     
m.get(hello)  // Hello ES6!

4.has(key)hasメソッドは、キーが現在のMapオブジェクトにあるかどうかを示すブール値を返します.
const m = new Map();
m.set('edition', 6);
m.set(262, 'standard');
m.set(undefined, 'nah');
m.has('edition')     // true
m.has('years')       // false
m.has(262)           // true
m.has(undefined)     // true

5.delete(key)deleteメソッドキーを削除し、trueを返します.削除に失敗した場合はfalseを返します.
const m = new Map();
m.set(undefined, 'nah');
m.has(undefined)     // true
m.delete(undefined)
m.has(undefined)       // false

6.clear()clearメソッドは、すべてのメンバーを消去し、値を返さない.
let map = new Map();
map.set('foo', true);
map.set('bar', false);
map.size // 2
map.clear()
map.size // 0

三、Map遍歴方法


Map構造オリジナルは、3つの遍歴生成関数と1つの遍歴方法:keys():キー名を返す遍歴器を提供します.values():キー値を返す遍歴器.entries():すべてのメンバーの遍歴器を返します.forEach():Mapのすべてのメンバーを巡回します.特に注意が必要なのは,Mapの遍歴順序が挿入順序であることである.
const map = new Map([
  ['F', 'no'],
  ['T',  'yes'],
]);

for (let key of map.keys()) {
  console.log(key);
}
// "F"
// "T"

for (let value of map.values()) {
  console.log(value);
}
// "no"
// "yes"

for (let item of map.entries()) {
  console.log(item[0], item[1]);
}
// "F" "no"
// "T" "yes"

//   
for (let [key, value] of map.entries()) {
  console.log(key, value);
}
// "F" "no"
// "T" "yes"

//      map.entries()
for (let [key, value] of map) {
  console.log(key, value);
}
// "F" "no"
// "T" "yes"

四、WeakMap


WeakMap構造はMap構造と類似しており,キー値ペアを生成するための集合でもある.
// WeakMap      set       
const wm1 = new WeakMap();
const key = {foo: 1};
wm1.set(key, 2);
wm1.get(key) // 2

// WeakMap          ,
//          
const k1 = [1, 2, 3];
const k2 = [4, 5, 6];
const wm2 = new WeakMap([[k1, 'foo'], [k2, 'bar']]);
wm2.get(k2) // "bar"

WeakMapとMapの違いは2つあります.まず、WeakMapはオブジェクトのみをキー名(nullを除く)として受け入れ、他のタイプの値はキー名として受け入れません.
const map = new WeakMap();
map.set(1, 2)
// TypeError: 1 is not an object!
map.set(Symbol(), 2)
// TypeError: Invalid value used as weak map key
map.set(null, 2)
// TypeError: Invalid value used as weak map key

上記のコードでは、値1とSymbol値をWeakMapのキー名として使用すると、エラーが発生します.次に,WeakMapのキー名が指すオブジェクトは,ゴミ回収メカニズムに計上されない.WeakMapの設計目的は、あるオブジェクトにデータを格納したい場合がありますが、このオブジェクトへの参照が形成されます.次の例を見てください.
const e1 = document.getElementById('foo');
const e2 = document.getElementById('bar');
const arr = [
  [e1, 'foo   '],
  [e2, 'bar   '],
];

上記のコードでは、e 1とe 2は2つのオブジェクトであり、arr配列を介して2つのオブジェクトにいくつかの文字の説明を追加します.これによりarrのe 1およびe 2への参照が形成される.この2つのオブジェクトが不要になったら、このリファレンスを手動で削除する必要があります.そうしないと、ゴミ回収メカニズムはe 1とe 2が消費するメモリを解放しません.
//     e1   e2    
//         
arr [0] = null;
arr [1] = null;

五、Mapと他のデータ構造の相互変換


1.Mapを配列に変換:Mapを配列に変換する最も便利な方法は、拡張演算子(...)を使用することです.
const myMap = new Map()
  .set(true, 7)
  .set({foo: 3}, ['abc']);
[...myMap]
// [ [ true, 7 ], [ { foo: 3 }, [ 'abc' ] ] ]

2.配列をMapに変換:配列をMapコンストラクタに転送すると、Mapに変換できます.
new Map([
  [true, 7],
  [{foo: 3}, ['abc']]
])
// Map {
//   true => 7,
//   Object {foo: 3} => ['abc']
// }

3.Mapをオブジェクトに変換:すべてのMapのキーが文字列の場合、オブジェクトに変換できます.
function strMapToObj(strMap) {
  let obj = Object.create(null);
  for (let [k,v] of strMap) {
    obj[k] = v;
  }
  return obj;
}

const myMap = new Map()
  .set('yes', true)
  .set('no', false);
strMapToObj(myMap)
// { yes: true, no: false }

4.オブジェクトをMapに変換するには:
function objToStrMap(obj) {
  let strMap = new Map();
  for (let k of Object.keys(obj)) {
    strMap.set(k, obj[k]);
  }
  return strMap;
}

objToStrMap({yes: true, no: false})
// Map {"yes" => true, "no" => false}

5.MapからJSONへの移行:MapからJSONへの移行は2つのケースを区別します.1つは、Mapのキー名が文字列である場合、対象JSONへの移行を選択することができる.
function strMapToJson(strMap) {
  return JSON.stringify(strMapToObj(strMap));
}

let myMap = new Map().set('yes', true).set('no', false);
strMapToJson(myMap)
// '{"yes":true,"no":false}'

もう1つは、Mapのキー名に非文字列がある場合、配列JSONに移行することを選択できます.
function mapToArrayJson(map) {
  return JSON.stringify([...map]);
}

let myMap = new Map().set(true, 7).set({foo: 3}, ['abc']);
mapToArrayJson(myMap)
// '[[true,7],[{"foo":3},["abc"]]]'

6.JSONからMapへ:JSONからMapへ、通常はすべてのキー名が文字列です.
function jsonToStrMap(jsonStr) {
  return objToStrMap(JSON.parse(jsonStr));
}

jsonToStrMap('{"yes": true, "no": false}')
// Map {'yes' => true, 'no' => false}

しかし、JSON全体が1つの配列であり、各配列メンバー自体が2つのメンバーを持つ配列であるという特殊な状況がある.このとき,1つ1つに対応してMapに変換できる.これは往々にして配列がJSONに変わる逆操作である.
function jsonToMap(jsonStr) {
  return new Map(JSON.parse(jsonStr));
}

jsonToMap('[[true,7],[{"foo":3},["abc"]]]')
// Map {true => 7, Object {foo: 3} => ['abc']}