摘要:返回一個布爾值,表示該值是否為的成員。清除所有成員,沒有返回值。該數組中的每一項也必須是數組,內部數組的首個項會作為鍵,第二項則為對應值。返回所有成員的遍歷器需要特別注意的是,的遍歷順序就是插入順序。
Js大部分歷史時期都只存在一種集合類型,也就是數組類型。數組在 JS 中的使用正如其他語言的數組一樣,但缺少更多類型的集合導致數組也經常被當作隊列與棧來使用。數組只使用了數值型的索引,而如果非數值型的索引是必要的,開發者便會使用非數組的對象。這種技巧引出了非數組對象的定制實現,即 Set 與 Map 。
Set: Set概述? Set結構類似于數組,但是沒有重復結構
? Set會自動移除重復的值,因此可以用來過濾數組中的重復結構
? Set內的對象是強引用
a) let set = new Set([1, 2, 3, 4]);
Set 構造器實際上可以接收任意可迭代對象作為參數。能使用數組是因為它們默認就是可迭代的,Set與Map也是一樣,Set構造器會使用迭代器來提取參數中的值
b) let set = new Set();
set.add(1);
set.add(2);
set.add(2);
//如果add對相同的值進行了多次調用,那么那么在第一次之后的調用實際上會被忽略
set.add(3);
set.add("3");
// Set 不會使用強制類型轉換來判斷值是否重復。這意味著 Set 可以同時包含數值 3 與 字符串 "3" ,將它們都作為相對獨立的項.
Set判斷是否重復使用了Object.is() 方法,唯一的例外是 +0 與 -0 在 Set 中被判斷為是相等的
c) 還可以向Set添加多個值相同的對象,他們不會被合并為同一項。
let set = new Set();
let key1 = {};
let key2 = {};
set.add(key1);
set.add(key2);
Set.prototype.constructor:構造函數,默認就是Set函數。
Set.prototype.size:返回Set實例的成員總數。
Set的方法分為兩類:操作方法 和 遍歷方法
2.1、操作方法:? add(value):添加某個值,返回 Set 結構本身。
因為返回set本身,所以可以寫成:
set.add(1).add(2).add(3)
? delete(value):刪除某個值,返回一個布爾值,表示刪除是否成功。
set.delete(2) ; // true
? has(value):返回一個布爾值,表示該值是否為Set的成員。
set.has(2) ; // false
? clear():清除所有成員,沒有返回值。
forEach():使用回調函數遍歷每個成員
es5給數組添加了forEach()方法,使得更容易處理數組中的每一項,沒有返回值
對于數組來說forEach的三個參數:
arr[].forEach(function(value,index,array){//do})
value數組中的當前項, index當前項的索引, array原始數組
但是對于Set來說:
let set2 = new Set([{"a":1}, {"b":2}]);
set2.forEach(function(value, key, ownerSet) {
console.log(ownerSet === set2);
console.log(value === key);
});
key 與 value 總是相同的,同時 ownerSet 也始終等于 set 。此代碼輸出:
true
true
true
true
如果想在回調函數中使用當前的this,還可以在forEach中傳入this作為第二個參數
let set = new Set([1, 2]);
let processor = {
output(value) { console.log(value);
},
process(dataSet) {
dataSet.forEach(function(value) { this.output(value); }, this);
}
};
processor.process(set);
本例中,在processor.process方法中的set調用了forEach方法,并傳遞了this作為第二個參數,這樣便能正確的調用到this.output()方法
或者也可以使用箭頭函數來達到相同的效果,無需傳入this,只需將上邊的process改寫成:
process(dataSet) {
dataSet.forEach((value) => this.output(value));
}
keys()、values()和entries()
keys():返回鍵名的遍歷器
values():返回鍵值的遍歷器
entries():返回鍵值對的遍歷器
keys方法、values方法、entries方法返回的都是遍歷器對象,由于Set結構沒有鍵名,只有鍵值(或者說鍵名和鍵值是同一個值),所以keys方法和values方法的行為完全一致。
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"]
上面代碼中,entries方法返回的遍歷器,同時包括鍵名和鍵值,所以每次輸出一個數組,它的兩個成員完全相等。
Set 結構的實例默認可遍歷,它的默認遍歷器生成函數就是它的values方法。
Set.prototype[Symbol.iterator] === Set.prototype.values
// true
這就意味著,可以省略values方法,直接用for…of循環遍歷Set
let set = new Set(["red", "green", "blue"]);
for (let x of set) {
console.log(x);
}
// red
// green
// blue
Set最經典的應用就是數組排重,
Set的內部實現Set的對象通過指針指向的內存中的地址來做比較,所以要判斷對象是否相等,只能通過遍歷去判斷,這其中包含了很多邊際情況。所以set沒有去判斷這些,只要往里面扔一個對象,那么就是一個新的元素
Set與其他數據結構的互相轉換 1、 Set與數組的轉換 1.1、 Set轉數組使用擴展運算符可以很簡單的將Set轉成數組
let set = new Set([1, 2, 3, 3, 3, 4, 5]),
let array = [...set];
console.log(array); // [1,2,3,4,5]
該示例很好的說明了Set的一個重要的功能,數組排重,當然也很好的展示了,如何從Set轉成數組的過程。
注意,這種轉變是生成了一個新的數組對象原來的Set依然存在。
如最初的構造示例:
let set = new Set([1,4,5,6,7]);
主要寫自身特性和與Set的區別?
驗證:外部賦空看內部是否變化?
外部回收后?
由于 Set 類型存儲對象引用的方式,它也可以被稱為 Strong Set 。對象存儲在 Set 的一個實例中時,實際上相當于把對象存儲在變量中。只要對于 Set 實例的引用仍然存在,所存儲的對象就無法被垃圾回收機制回收,從而無法釋放內存。
當 JS 代碼在網頁中運行,同時你想保持與 DOM 元素的聯系,在該元素可能被其他腳本移除的情況下,你應當不希望自己的代碼保留對該 DOM 元素的最后一個引用(這種情況被稱為內存泄漏)
為了緩解這個問題, ES6 也包含了 Weak Set ,該類型只允許存儲對象弱引用,而不能存儲基本類型的值。對象的弱引用在它自己成為該對象的唯一引用時,不會阻止垃圾回收
WeakSet 結構有以下三個方法。
WeakSet.prototype.add(value):向 WeakSet 實例添加一個新成員。
WeakSet.prototype.delete(value):清除 WeakSet 實例的指定成員。
WeakSet.prototype.has(value):返回一個布爾值,表示某個值是否在 WeakSet 實例之中。
對于 WeakSet 的實例,若調用 add() 方法時傳入了非對象的參數,就會拋出錯誤(
has() 或 delete() 則會在傳入了非對象的參數時返回 false );
Weak Set 不可迭代,因此不能被用在 for-of 循環中;
Weak Set 無法暴露出任何迭代器(例如 keys() 與 values() 方法),因此沒有任何編
程手段可用于判斷 Weak Set 的內容;
Weak Set 沒有 forEach() 方法;
Weak Set 沒有 size 屬性。
WeakSet之所以不可遍歷是由于 WeakSet 內部有多少個成員,取決于垃圾回收機制有沒有運行,運行前后很可能成員個數是不一樣的,而垃圾回收機制何時運行是不可預測的,因此 ES6 規定 WeakSet 不可遍歷。
WeakSet常用場景
Weak Set 看起來功能有限,而這對于正確管理內存而言是必要的。一般來說,若只想追蹤對象的引用,應當使用 Weak Set 而不是正規 Set
如果Set中引用了不再需要的大型對象,如已經從DOM樹中刪除的DOM元素,那么其回收代價是昂貴的
JavaScript 的對象(Object),本質上是鍵值對的集合(Hash 結構),但是傳統上只能用字符串當作鍵。這給它的使用帶來了很大的限制。
為了解決這個問題,ES6 提供了 Map 數據結構。它類似于對象,也是鍵值對的集合,但是“鍵”的范圍不限于字符串,各種類型的值(包括對象)都可以當作鍵。也就是說,Object 結構提供了“字符串—值”的對應,Map 結構提供了“值—值”的對應,是一種更完善的 Hash 結構實現。如果你需要“鍵值對”的數據結構,Map 比 Object 更合適。
ES6 的 Map 類型是鍵值對的有序列表,而鍵和值都可以是任意類型。鍵的比較使用的是Object.is() ,因此你能將 5 與 "5" 同時作為鍵,因為它們類型不同。這與使用對象屬性
作為鍵的方式(指的是用對象來模擬 Map )截然不同,因為對象的屬性會被強制轉換為字符串。
你可以調用 set() 方法并給它傳遞一個鍵與一個關聯的值,來給 Map 添加項;此后使用鍵名來調用 get() 方法便能提取對應的值。例如:
let map = new Map();
map.set("title", "Understanding ES6");
map.set("year", 2016);
console.log(map.get("title")); // "Understanding ES6"
console.log(map.get("year")); // 2016
依然與 Set 類似,你能將數組傳遞給 Map 構造器,以便使用數據來初始化一個 Map 。該數組中的每一項也必須是數組,內部數組的首個項會作為鍵,第二項則為對應值。因此整個Map 就被這些雙項數組所填充。例如:
let map = new Map([["name", "Nicholas"], ["age", 25]]);
console.log(map.has("name")); // true
console.log(map.get("name")); // "Nicholas"
console.log(map.size); // 2
通過構造器中的初始化, "name" 與 "age" 這兩個鍵就被添加到 map 變量中。雖然由數組構成的數組看起來有點奇怪,這對于準確表示鍵來說卻是必要的:因為鍵允許是任意數據類型,將鍵存儲在數組中,是確保它們在被添加到 Map 之前不會被強制轉換為其他類型的唯一方法。
Map構造函數接受數組作為參數,實際上執行的是下面的算法。
const items = [
["name", "Nicholas"]
["age", 25] ]
const map = new Map();
items.forEach(
([key, value]) => map.set(key, value)
);
事實上,不僅僅是數組,任何具有 Iterator 接口、且每個成員都是一個雙元素的數組的數據結構都可以當做Map構造函數的參數。這就是說,Set和Map都可以用來生成新的Map
其他構造const set = new Set([
["foo", 1],
["bar", 2]
]);
const m1 = new Map(set);
m1.get("foo") // 1
const m2 = new Map([["baz", 3]]);
const m3 = new Map(m2);
m3.get("baz") // 3
上面代碼中,我們分別使用 Set 對象和 Map 對象,當作Map構造函數的參數,結果都生成了新的 Map 對象。
Map 同樣擁有 size 屬性,用于指明包含了多少個鍵值對。以下代碼用不同方式使用了這三種方法以及 size 屬性:
2方法 2.1、操作方法set方法設置鍵名key對應的鍵值為value,然后返回整個 Map 結構。如果key已經有值,則鍵值會被更新,否則就新生成該鍵。
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");
get方法讀取key對應的鍵值,如果找不到key,返回undefined。
const m = new Map();
const hello = function() {console.log("hello");};
m.set(hello, "Hello ES6!") // 鍵是函數
m.get(hello) // Hello ES6!
2.13、has、delete、clearMap 與 Set 共享了幾個方法,這是有意的,允許你使用相似的方式來與 Map 及 Set 進行交互。以下三個方法在 Map 與 Set 上都存在:
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
delete方法刪除某個鍵,返回true。如果刪除失敗,返回false。
const m = new Map();
m.set(undefined, "nah");
m.has(undefined) // true
m.delete(undefined)
m.has(undefined) // false
clear方法清除所有成員,沒有返回值。
let map = new Map();
map.set("foo", true);
map.set("bar", false);
map.size // 2
map.clear()
map.size // 0
? keys():返回鍵名的遍歷器。
? values():返回鍵值的遍歷器。
? entries():返回所有成員的遍歷器
需要特別注意的是,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"
上面代碼最后的那個例子,表示 Map 結構的默認遍歷器接口(Symbol.iterator屬性),就是entries方法。
Map 的 forEach() 方法類似于 Set 與數組的同名方法,它接受一個能接收三個參數的回調函數:
Map 中下個位置的值;
該值所對應的鍵;
目標 Map 自身。
回調函數的這些參數更緊密契合了數組 forEach() 方法的行為,即:第一個參數是值、第二個參數則是鍵(數組中的鍵是數值索引)。此處有個示例:
let map = new Map([
["name", "Nicholas"],
["age", 25]
]);
map.forEach(function(value, key, ownerMap) {
console.log(key + " " + value);
console.log(ownerMap === map);
});
forEach() 的回調函數輸出了傳給它的信息。其中 value 與 key 被直接輸出, ownerMap
與 map 進行了比較,說明它們是相等的。這就輸出了:
name Nicholas
true
age 25
true
Map 結構轉為數組結構,比較快速的方法是使用擴展運算符(...)。
const map = new Map([
[1, "one"],
[2, "two"],
[3, "three"],
]);
[...map.keys()]
// [1, 2, 3]
[...map.values()]
// ["one", "two", "three"]
[...map.entries()]
// [[1,"one"], [2, "two"], [3, "three"]]
[...map]
// [[1,"one"], [2, "two"], [3, "three"]]
將數組傳入 Map 構造函數,就可以轉為 Map。
new Map([
[true, 7],
[{foo: 3}, ["abc"]]
])
// Map {
// true => 7,
// Object {foo: 3} => ["abc"]
// }
如果所有 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 }
如果有非字符串的鍵名,那么這個鍵名會被轉成字符串,再作為對象的鍵名。
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}
Map 轉為 JSON 要區分兩種情況。一種情況是,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}"
另一種情況是,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"]]]"
JSON 轉為 Map,正常情況下,所有鍵名都是字符串。
function jsonToStrMap(jsonStr) {
return objToStrMap(JSON.parse(jsonStr));
}
jsonToStrMap("{"yes": true, "no": false}")
// Map {"yes" => true, "no" => false}
但是,有一種特殊情況,整個 JSON 就是一個數組,且每個數組成員本身,又是一個有兩個成員的數組。這時,它可以一一對應地轉為 Map。這往往是 Map 轉為數組 JSON 的逆操作。
function jsonToMap(jsonStr) {
return new Map(JSON.parse(jsonStr));
}
jsonToMap("[[true,7],[{"foo":3},["abc"]]]")
// Map {true => 7, Object {foo: 3} => ["abc"]}
WeakMap與Map的區別有兩點。
首先,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
其次,WeakMap的鍵名所指向的對象,不計入垃圾回收機制。
WeakMap的設計目的在于,有時我們想在某個對象上面存放一些數據,但是這會形成對于這個對象的引用。請看下面的例子。
const e1 = document.getElementById("foo");
const e2 = document.getElementById("bar");
const arr = [
[e1, "foo 元素"],
[e2, "bar 元素"],
];
上面代碼中,e1和e2是兩個對象,我們通過arr數組對這兩個對象添加一些文字說明。這就形成了arr對e1和e2的引用。
一旦不再需要這兩個對象,我們就必須手動刪除這個引用,否則垃圾回收機制就不會釋放e1和e2占用的內存。
// 不需要 e1 和 e2 的時候
// 必須手動刪除引用
arr [0] = null;
arr [1] = null;
上面這樣的寫法顯然很不方便。一旦忘了寫,就會造成內存泄露。
WeakMap 就是為了解決這個問題而誕生的,它的鍵名所引用的對象都是弱引用,即垃圾回收機制不將該引用考慮在內。因此,只要所引用的對象的其他引用都被清除,垃圾回收機制就會釋放該對象所占用的內存。也就是說,一旦不再需要,WeakMap 里面的鍵名對象和所對應的鍵值對會自動消失,不用手動刪除引用。
ES6 的 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的屬性和方法:
WeakMap 與 Map 在 API 上的區別主要是兩個,一是沒有遍歷操作(即沒有keys()、values()和entries()方法),也沒有size屬性。因為沒有辦法列出所有鍵名,某個鍵名是否存在完全不可預測,跟垃圾回收機制是否運行相關。這一刻可以取到鍵名,下一刻垃圾回收機制突然運行了,這個鍵名就沒了,為了防止出現不確定性,就統一規定不能取到鍵名。二是無法清空,即不支持clear方法。因此,WeakMap只有四個方法可用:get()、set()、has()、delete()。
const wm = new WeakMap();
// size、forEach、clear 方法都不存在
wm.size // undefined
wm.forEach // undefined
wm.clear // undefined
Weak Map 的最佳用武之地,就是在瀏覽器中創建一個關聯到特定 DOM 元素的對象。例如,某些用在網頁上的 JS 庫會維護一個自定義對象,用于引用該庫所使用的每一個 DOM 元素,并且其映射關系會存儲在內部的對象緩存中。
該方法的困難之處在于:如何判斷一個 DOM 元素已不復存在于網頁中,以便該庫能移除此元素的關聯對象。若做不到,該庫就會繼續保持對 DOM 元素的一個無效引用,并造成內存泄漏。使用 Weak Map 來追蹤 DOM 元素,依然允許將自定義對象關聯到每個 DOM 元素,而在此對象所關聯的 DOM 元素不復存在時,它就會在 Weak Map 中被自動銷毀。
必須注意的是, Weak Map 的鍵才是弱引用,而值不是。在 Weak Map 的值中存儲對象會阻止垃圾回收,即使該對象的其他引用已全都被移除。
當決定是要使用 Weak Map 還是使用正規 Map 時,首要考慮因素在于你是否只想使用對象類型的鍵。如果你打算這么做,那么最好的選擇就是 Weak Map 。因為它能確保額外數據在不再可用后被銷毀,從而能優化內存使用并規避內存泄漏。
要記住 Weak Map 只為它們的內容提供了很小的可見度,因此你不能使用 forEach() 方法、size 屬性或 clear() 方法來管理其中的項。如果你確實需要一些檢測功能,那么正規 Map會是更好的選擇,只是一定要確保留意內存的使用。
//map和set 比較的方法是不是一樣
NaN 和 +0 -0
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/96813.html
摘要:學習筆記工作中常用到的語法只是簡單提及和,今天有空于是寫了這篇文章深入理解中的和數據結構,與其它數據結構的互相轉換。的提供了新的數據結構。本身是一個構造函數,用來生成數據結構。 文中的內容主要是來自于阮一峰的《ES6標準入門》(第三版)。《學習ES6筆記──工作中常用到的ES6語法》只是簡單提及Set和Map,今天有空于是寫了這篇文章──《深入理解:ES6中的Set和Map數據結構,M...
摘要:創建并添加項目可以使用數組來初始化一個,并且構造器會確保不重復地使用這些值使用方法來測試某個值是否存在于中移除值使用方法來移除單個值,或調用方法來將所有值從中移除。屬性的初始化將數組傳遞給構造器,以便使用數據來初始化一個。 主要知識點:Set的基本操作,Weak Set,Map的基本操作,Weak MapshowImg(https://segmentfault.com/img/bVbf...
摘要:引入的數據結構新加入的數據類型有這些數據結構的支持并不廣泛,在寫這篇文章的時候。是或其他可枚舉的對象,其每個元素是的元數組。開頭的和不對持有引用,不影響。因此,他們沒有辦法對自身的進行直接的枚舉。目前新版的和支持。 原文:http://pij.robinqu.me/JavaScript_Core/ECMAScript/es6/es6_data_types.html 源代...
摘要:中模擬與長久以來,數組一直是中唯一的集合類型。用數組初始化集合事實上,只要是可迭代對象數組集合集合,都可以作為構造函數的參數。構造函數通過迭代器從參數中提取值。 ES5中模擬Set與Map 長久以來,數組一直是JavaScript中唯一的集合類型。如果開發者們需要使用非數值型索引,就會用非數組對象創建所需的數據結構,而這就是Set集合與Map集合的早期實現。 一般來說,Set集合常被用...
摘要:提供了新的數據結構。用法結構有以下屬性構造函數,默認就是函數。結構有以下方法添加某個值,返回結構本身。返回一個布爾值,表示該值是否為的成員。清除所有成員,沒有返回值。 Set ES6 提供了新的數據結構 Set。它類似于數組,但是成員的值都是唯一的,沒有重復的值。 用法:new Set([iterable]) const set = new Set([1, 2, 3, 4, 4, 4]...
摘要:由于和不會被轉換為字符串,所以在內部是不同的項,如果他們被轉化為字符串,那么都會等于,如果多次調用并傳入相同的值作為參數。第二次重復傳入并不會被添加到集合中,那么的屬性值還是為。的方法和共享了幾個方法。小結正式將與引入。 se5中的set與map 在est5中開發者使用對象屬性來模擬。set多用于檢查鍵的存在,map多用于提取數據。 { let set = Object.cre...
閱讀 2508·2023-04-25 19:31
閱讀 2244·2021-11-04 16:11
閱讀 2812·2021-10-08 10:05
閱讀 1520·2021-09-30 09:48
閱讀 2319·2019-08-30 15:56
閱讀 2415·2019-08-30 15:56
閱讀 2177·2019-08-30 15:53
閱讀 2272·2019-08-30 15:44