1.セットコレクション
長い間、JS は複数のデータを保存するために配列とオブジェクトしか使用できず、他の言語のような豊富なコレクション型がありませんでした。したがって、ES6 では、さまざまなシナリオで役割を果たす 2 つの新しいコレクション タイプ (セットとマップ) が追加されています。
1) set は一意のデータを保存するために使用されます
1) セットコレクションの作成方法
new Set(); // 创建一个没有任何内容的set集合
new Set(iterable); // 创建一个具有初始内容的set集合,内容来自于可迭代对象每一次迭代的结果
2) セットコレクションに対して後続の操作を実行する方法
- add(data): セットコレクションの末尾にデータを追加します。データがすでに存在する場合、操作は実行されません。
- set は Object.is を使用して 2 つのデータが同じかどうかを判断しますが、+0 と -0 の場合、set はそれらが等しいとみなします。
- has(data): セット内に対応するデータがあるかどうかを判断します
- delete(data): 一致するデータを削除し、削除が成功したかどうかを返します。
- clear(): セットコレクション全体をクリアします
- size: セット コレクション内の要素の数を取得します。これは読み取り専用の属性であり、再割り当てすることはできません。
3) 配列との間の変換方法
const s = new Set([x,x,x,x,x]);
// set本身也是一个可迭代对象,每次迭代的结果就是每一项的值
const arr = [...s];
4) トラバース方法
- for-of ループを使用する
- セット内のインスタンス メソッド forEach を使用する
注: セット コレクションには添え字がないため、forEach のコールバックの 2 番目のパラメーターと最初のパラメーターは一貫しており、両方ともセット内の各項目を表します。
2) セットコレクションの適用
2 つの配列の和集合と共通部分を見つけます
const arr1 = [33, 43, 55, 66, 11, 33, 5, 11];
const arr2 = [44, 22, 3, 11, 45, 66, 78, 2, 23];
// 求并集
const allArr = [...new Set([...arr1, ...arr2])];
// 求交集
const noRepeatArr1 = new Set(arr1);
noRepeatArr1.forEach((item) => {
if (arr2.includes(item)) {
return;
}
noRepeatArr1.delete(item);
});
console.log([...noRepeatArr1]);
2.手書きセット
コードは次のとおりで、対応する関数を実装するために純粋に手書きで書かれています。
// 模拟Set集合
class MySet {
constructor(iterator = []) {
// 判断iterator是否为可迭代对象
if (typeof iterator[Symbol.iterator] !== "function") {
throw new TypeError(`${
typeof iterator} ${
iterator} is not iterable`);
}
this._datas = [];
for (const item of iterator) {
this.add(item);
}
}
/**
* 判断集合中是否有data数据
* @param {any} data
*/
has(data) {
for (const item of this._datas) {
if (this.isEqual(item, data)) {
return true;
}
}
return false;
}
/**
* 删除集合中的数据
* @param {any} data
*/
delete(data) {
for (const item of this._datas) {
if (this.isEqual(data, item)) {
this._datas.splice(this._datas.indexOf(data), 1);
return true;
}
}
return false;
}
/**
* 只读size属性
*/
get size() {
return this._datas.length;
}
/**
* 清空集合
*/
clear() {
this._datas = [];
}
/**
* 向集合中添加一个数据
* @param {any} data 数据
*/
add(data) {
if (this.has(data)) {
return;
}
this._datas.push(data);
}
/**
* 循环函数
* @param {any} callback 回调函数
*/
forEach(callback) {
for (let i = 0; i < this._datas.length; i++) {
callback(this._datas[i], this._datas[i], this);
}
}
/**
* 将其变为生成器函数
*/
*[Symbol.iterator]() {
for (const item of this._datas) {
yield item;
}
}
/**
* 判断两个数是否相等,不限类型
* @param {any} value1
* @param {any} value2
*/
isEqual(value1, value2) {
if (value1 === 0 && value2 === 0) {
return true;
} else {
return Object.is(value1, value2);
}
}
}
テストコードは以下の通りです
const s = new MySet([1, 2, 3, 4, 3]);
s.forEach((a1, a2, a3) => {
console.log(a1, a2, a3);
});
3. マップコレクション
キーと値のペアのデータ収集の特徴: キーを繰り返すことはできません
マップ コレクションは、複数のキーと値のペアを保存するために特別に使用されます。
マップが登場する前は、オブジェクトを使用してキーと値のペアを格納していました。キーは属性名、値は属性値でした。
オブジェクト ストレージの使用には次の問題があります。
- キー名は文字列のみにすることができます
- データ量を取得するのが不便
- キー名はプロトタイプ上の名前と競合しやすい
1) マップの作成方法
new Map(); //创建一个空的map
new Map(iterable); //创建一个具有初始内容的map,初始内容来自于可迭代对象每一次迭代的结果,但是,它要求每一次迭代的结果必须是一个长度为2的数组,数组第一项表示键,数组的第二项表示值
各反復の結果は必ずしも長さ 2 の配列である必要はありませんが、2 回反復できるデータ型である必要があります。
const mp1 = new Map([["a", 0], [3, 2], [{
}, 9]);
2) その後の操作方法
- size: 読み取り専用属性、現在のマップ内のキーの数を取得します
- set(key, value): キーと値のペアを設定します。キーと値は任意のタイプにすることができます。
- キーが存在しない場合は項目を追加します
- キーがすでに存在する場合は、その値を変更します
- キーを比較する方法は set と同じです ({} と {} は 2 つのオブジェクトに属していることに注意してください)
- get(key): キーに基づいて対応する値を取得します。
- has(key): キーが存在するかどうかを判断します
- delete(key): 指定されたキーを削除します
- clear(): マップをクリアします
3) 配列との間の変換
セットと同じ
4) トラバース
- for-of、各反復の結果は長さ 2 の配列になります
- forEach、コールバック関数を通過します
- パラメータ1:各項目の値
- パラメータ2: 各項目のキー
- パラメータ 3: マップ自体
4. 手書きの地図
以下は、私が手作業で細断したコードです。これは、Map コレクションの性質を完全に模倣し、完全に私自身によって完成されました。
class MyMap {
constructor(iterator) {
// Whether the first of iterator is iterable or not.
if (typeof iterator[Symbol.iterator] !== "function") {
// The first floor of iterator is not iterabl, throw an Error of Type.
throw new TypeError(`${
typeof iterator} ${
iterator} is not iterable`);
}
// Create an object to store Map datas
this._datas = [];
// The first floor of iterator is iterable
for (const item of iterator) {
// Whether the second floor of iterator is iterable or not
if (typeof item[Symbol.iterator] !== "function") {
// The second floor of iterator is not iterable, throw an Error of Type.
throw new TypeError(`Iterator value ${
item} is not an entry object`);
}
// The parameter "iterator" is valid.
this.set(...item);
}
}
/**
* Is there a key named "key" in MyMap? This is a judging function.
* @param {any} key
* @returns
*/
has(key) {
for (const item of this._datas) {
if (item.key === key) {
return true;
}
}
return false;
}
/**
* Get MyMap item's value according to keyName.
* @param {any} keyName
* @returns value or undefined
*/
get(keyName) {
for (const {
key, value } of this._datas) {
if (keyName === key) {
return value;
}
}
return undefined;
}
/**
* Set the map data occording to "key" and "value"
* @param {any} key
* @param {any} value
*/
set(key, value) {
// Whether object {key, value} is still in Array this._datas or not
for (const item of this._datas) {
if (item.key === key && item.value === value) {
// Yes. There is
return;
}
// No. There isn't.
// Then judge whether object includes "key" has already in Array this._datas
if (item.key === key) {
// Yes. It is. And Modify the value of key
item.value = value;
return;
}
}
// No!And then add it normally
this._datas.push({
key,
value,
});
}
/**
* Delete key in MyMap
* @param {any} key
* @returns {Boolean} Delete successfully?
*/
delete(key) {
// Whether there is key in this._datas
if (!this.has(key)) {
return false;
}
// Find the item with "key" and then delete it.
for (let i = 0; i < this._datas.length; i++) {
// Find the item with "key"
if (this._datas[i].key === key) {
this._datas.splice(i, 1);
return true;
}
}
}
/**
* Read only. Get the number of keys in MyMap
* @returns {Number} The number of keys in MyMap
*/
get size() {
return this._datas.length;
}
/**
* Clear the items in MyMap
*/
clear() {
this._datas = [];
}
/**
* Add iterable attribute for MyMap. Now the example of MyMap can be iterable.
*/
*[Symbol.iterator]() {
for (const item of this._datas) {
yield item;
}
}
/**
* forEach loop function, you can do what you want in callback function!
* @param {Function} callback
*/
forEach(callback) {
for (const {
key, value } of this._datas) {
callback(value, key, this);
}
}
}
テストコードは以下の通りです
const mp = new MyMap([
[1, 2],
["a", 3],
]);
// for (const item of mp) {
// console.log(item);
// }
mp.forEach((a1, a2, a3) => {
console.log(a1, a2, a3);
});
5.WeakSetとWeakMap
これら 2 つのキーワードが作成されますが、これらは実際にはオブジェクトがまだ存在するかどうかを監視するものと考えられます。つまり、オブジェクトのガベージ コレクション メカニズムには影響しません。
ウィークセット
このコレクションを使用すると、set と同じ機能を実現できますが、違いは次のとおりです。
- 内部に保存されたオブジェクト アドレスはガベージ コレクションに影響しません。
- 追加できるのはオブジェクトのみです
- トラバースできません (反復可能なオブジェクトではありません)、サイズ属性も forEach メソッドもありません
ウィークマップ
マップ コレクションと似ていますが、次の点が異なります。
- キーが保存されているアドレスはガベージ コレクションに影響しません。
- そのキーはオブジェクトのみにすることができます
- トラバースできません (反復可能なオブジェクトではありません)、サイズ属性も forEach メソッドもありません
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<ul>
<!-- { id:"1", name:"姓名1" } -->
<li>1</li>
<!-- { id:"2", name:"姓名2" } -->
<li>2</li>
<!-- { id:"3", name:"姓名3" } -->
<li>3</li>
</ul>
<script>
const wmap = new WeakMap();
let lis = document.querySelectorAll("li");
for (const li of lis) {
wmap.set(li, {
id: li.innerHTML,
name: `姓名${
li.innerHTML}`
});
}
lis[0].remove();
lis = null;
console.log(wmap);
</script>
</body>
</html>