每當去學習ES6的時候,我都會不禁自主的打開阮一峯老師的《ES6入門》去學習和查找用法,相信大多數同窗也都和我同樣看過阮老師寫的文章。es6
固然你們也都知道ES6裏經常使用的API就那麼些,很少很多,用在項目中也是剛恰好。算法
不過在每次讀到Set和Map數據結構那一章的時候,老是有點不知所措,由於我不明白實現這樣的數據結構,目的是什麼,意義又是什麼呢數組
Set和Map主要的應用場景在於數組去重和數據存儲,幸運的是在讀了關於數據結構和算法之類的書籍後,恍然大悟的發現bash
原來Set是一種叫作集合的數據結構,Map是一種叫作字典的數據結構數據結構
那麼下面就跟隨我一塊兒去了解下這兩種數據結構,最後來親手實現的一個ES6中的Set和Map吧數據結構和算法
const s = new Set();
[2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));
for (let i of s) {
console.log(i); // 2 3 5 4
}
// 去除數組的重複成員
let array = [1,2,1,4,5,3];
[...new Set(array)] // [1, 2, 4, 5, 3]
複製代碼
具體用法若是還有不清楚的,這裏我會在後面一一細說。如今仍是來看一下以ES6中Set類(數據結構)爲基礎實現的集合吧函數
function Set(arr = []) { // 能夠傳入數組
let items = {};
this.size = 0; // 記錄集合中成員的數量
}
module.exports = Set;
複製代碼
這裏用{}對象來表示集合,也是由於對象不容許一個鍵指向兩個不一樣的屬性,保證了集合裏的元素都是惟一的學習
接下來,就須要按照ES6中Set類的實現,添加一些集合的操做方法了測試
首先要實現的是has方法,由於在add和delete等其餘方法中都會被調用,下面來看一下它的實現ui
function Set() {
let items = {};
this.size = 0;
// has(val)方法
this.has = function(val) {
// 對象都有hasOwnProperty方法,判斷是否擁有特定屬性
return items.hasOwnProperty(val);
};
}
複製代碼
接下來要實現add方法
// add(val)方法
this.add = function(val) {
if (!this.has(val)) {
items[val] = val;
this.size++; // 累加集合成員數量
return true;
}
return false;
};
複製代碼
對於給定的val,能夠檢測是否存在於集合中
繼續寫着,這回把兩個都寫上
// delete(val)方法
this.delete = function(val) {
if (this.has(val)) {
delete items[val]; // 將items對象上的屬性刪掉
this.size--;
return true;
}
return false;
};
// clear方法
this.clear = function() {
items = {}; // 直接將集合賦一個空對象便可
this.size = 0;
};
複製代碼
在delete方法中,判斷val是否存在於集合中,若是存在就直接從集合中刪掉,返回true
以上完成的都是操做方法,下面咱們再來實現一下遍歷方法
這兩個方法咱們能夠放在一塊兒來實現,由於經過ES6對Object的擴展能夠輕鬆實現對應的方法,下面看一下具體實現,上代碼:
// keys()方法
this.keys = function() {
return Object.keys(items); // 返回遍歷集合的全部鍵名的數組
};
// values()方法
this.values = function() {
return Object.values(items); // 返回遍歷集合的全部鍵值的數組
};
複製代碼
使用一下看看
// set.js
const Set = require('./Set.js'); // 導入寫好的Set類
let set = new Set();
set.add(1);
set.add(3);
set.add(2);
console.log(set.keys()); // [ '1', '2', '3' ]
console.log(set.values()); // [ 1, 2, 3 ]
複製代碼
這裏咱們看到和ES6中的Set有點區別,由於Object的這幾個方法都是按照數值大小,從小到大遍歷的數組,因此你們知道這一點比較好,具體實現仍是有些不一樣的,哈哈
ES6中Set結構的實例上帶的forEach方法,其實和數組的forEach方法很類似,只不過Set結構的鍵名就是鍵值,因此第一個參數與第二個參數的值永遠都是同樣的
下面就按照實現數組的forEach方法,咱們來完成Set的forEach方法
// forEach(fn, context)方法
this.forEach = function(fn, context = this) {
for (let i = 0; i < this.size; i++) {
let item = Object.keys(items)[i];
fn.call(context, item, item, items);
}
};
複製代碼
使用forEach方法
// set.js
const Set = require('./Set.js');
let set = new Set();
set.add(1);
set.add(4);
set.add('3');
set.forEach((value, key) => console.log(key + ' : ' + value)); // 1:1, 3:3, 4:4
let arr = set.values(); // [ 1, 3, 4 ]
arr = new Set(arr.map(x => x * 2)).values();
console.log(arr); // [ 2, 6, 8 ]
複製代碼
基本上實現了Set結構的方法,不過,發現一個問題,那就是每次添加一個元素都要add這樣寫起來確實好麻煩,Set是能夠接收一個數組做爲參數的,那麼咱們把這個也實現一下
function Set(arr = []) { // 傳入接受的數組,若是沒有傳指定一個空數組作爲初始值
let items = {};
this.size = 0;
// has方法
this.has = function (val) {
return items.hasOwnProperty(val);
};
// add方法
this.add = function (val) {
// 若是沒有存在items裏面就能夠直接寫入
if (!this.has(val)) {
items[val] = val;
this.size++;
return true;
}
return false;
};
arr.forEach((val, i) => { // 遍歷傳入的數組
this.add(val); // 將數組裏每一項值添加到集合中
});
// 省略...
}
複製代碼
再來看看如今能不能支持傳入的數組了
// 間接使用map和filter
const Set = require('./Set.js');
let arr = new Set([1, 2, 3]).values();
m = new Set(arr.map(x => x * 2));
f = new Set(arr.filter(x => x>1));
console.log(m.values()); // [ 2, 4, 6 ]
console.log(f.values()); // [ 2, 3 ]
// 數組去重
let arr2 = new Set([3, 5, 2, 1, 2, 5, 5]).values();
console.log(arr2); // [ 1, 2, 3, 5 ]
複製代碼
如今咱們有了一個和ES6中很是相似的Set類實現。如前所述,也能夠用數組替代對象,存儲元素。喜歡動手的同窗們,以後也能夠去嘗試一下
除此以外,Set還能夠實現並集(union),交集(intersect),差集(difference)
作事仍是要作全套的,咱們也一一來實現一下吧
如圖所示:
如今先來實現union方法// 並集
this.union = function (other) {
let union = new Set();
let values = this.values();
for (let i = 0; i < values.length; i++) {
union.add(values[i]);
}
values = other.values(); // 將values從新賦值爲新的集合
for (let i = 0; i < values.length; i++) {
union.add(values[i]);
}
return union;
};
// 交集
this.intersect = function (other) {
let intersect = new Set();
let values = this.values();
for (let i = 0; i < values.length; i++) {
if (other.has(values[i])) { // 查看是否也存在於other中
intersect.add(values[i]); // 存在的話就像intersect中添加元素
}
}
return intersect;
};
複製代碼
再來看下difference差集的實現,以後一塊兒再測試一番
// 差集
this.difference = function (other) {
let difference = new Set();
let values = this.values();
for (let i = 0; i < values.length; i++) {
if (!other.has(values[i])) { // 將不存在於other集合中的添加到新的集合中
difference.add(values[i]);
}
}
return difference;
};
複製代碼
在此,先給你們貼一下完整的實現代碼
function Set(arr = []) {
let items = {};
this.size = 0;
// has方法
this.has = function (val) {
return items.hasOwnProperty(val);
};
// add方法
this.add = function (val) {
// 若是沒有存在items裏面就能夠直接寫入
if (!this.has(val)) {
items[val] = val;
this.size++;
return true;
}
return false;
};
arr.forEach((val, i) => {
this.add(val);
});
// delete方法
this.delete = function (val) {
if (this.has(val)) {
delete items[val]; // 將items對象上的屬性刪掉
this.size--;
return true;
}
return false;
};
// clear方法
this.clear = function () {
items = {};
this.size = 0;
};
// keys方法
this.keys = function () {
return Object.keys(items);
};
// values方法
this.values = function () {
return Object.values(items);
}
// forEach方法
this.forEach = function (fn, context = this) {
for (let i = 0; i < this.size; i++) {
let item = Object.keys(items)[i];
fn.call(context, item, item, items);
}
}
// 並集
this.union = function (other) {
let union = new Set();
let values = this.values();
for (let i = 0; i < values.length; i++) {
union.add(values[i]);
}
values = other.values(); // 將values從新賦值爲新的集合
for (let i = 0; i < values.length; i++) {
union.add(values[i]);
}
return union;
};
// 交集
this.intersect = function (other) {
let intersect = new Set();
let values = this.values();
for (let i = 0; i < values.length; i++) {
if (other.has(values[i])) {
intersect.add(values[i]);
}
}
return intersect;
};
// 差集
this.difference = function (other) {
let difference = new Set();
let values = this.values();
for (let i = 0; i < values.length; i++) {
if (!other.has(values[i])) {
difference.add(values[i]);
}
}
return difference;
};
// 子集
this.subset = function(other) {
if (this.size > other.size) {
return false;
} else {
let values = this.values();
for (let i = 0; i < values.length; i++) {
console.log(values[i])
console.log(other.values())
if (!other.has(values[i])) {
return false;
}
}
return true;
}
};
}
module.exports = Set;
複製代碼
寫了辣麼多一塊兒來測試一下吧
const Set = require('./Set.js');
let set = new Set([2, 1, 3]);
console.log(set.keys()); // [ '1', '2', '3' ]
console.log(set.values()); // [ 1, 2, 3 ]
console.log(set.size); // 3
set.delete(1);
console.log(set.values()); // [ 2, 3 ]
set.clear();
console.log(set.size); // 0
// 並集
let a = [1, 2, 3];
let b = new Set([4, 3, 2]);
let union = new Set(a).union(b).values();
console.log(union); // [ 1, 2, 3, 4 ]
// 交集
let c = new Set([4, 3, 2]);
let intersect = new Set([1,2,3]).intersect(c).values();
console.log(intersect); // [ 2, 3 ]
// 差集
let d = new Set([4, 3, 2]);
let difference = new Set([1,2,3]).difference(d).values();
// [1,2,3]和[4,3,2]的差集是1
console.log(difference); // [ 1 ]
複製代碼
目前爲止咱們用集合這種數據結構就實現了相似ES6中Set類,上面的使用方法也基本同樣,你們能夠以後有時間的話動手去敲一敲看一看,走過路過不能錯過
既然咱們已經完成了Set的實現,那麼好事要成雙,一氣呵成再把Map也一塊兒寫出來,天了擼的,開始
在數據結構還有一種結構叫作字典,它就是實現基於ES6中的Map類的結構
那麼集合又和字典有什麼區別呢:
因此這一下讓咱們明白了,Map其實的主要用途也是用於存儲數據的,相比於Object只提供「字符串—值」的對應,Map提供了「值—值」的對應。也就是說若是你須要「鍵值對」的數據結構,Map比Object更合適
下面來看一下基本使用:
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的基本使用,還有更多有用的方法稍後會隨着實現的深刻分別展現
屬性:
操做方法:
遍歷方法:
知道了都有哪些屬性和方法,那就閒言少敘,開始建立一個字典吧
function Map() {
let items = {};
}
module.exports = Map; // 導出
複製代碼
建立好了字典這個骨架,那就開始添加一些方法了
首當其衝的固然是has了,由於在set和get裏都會用到,實現思路和以前寫的集合也很相似
function Map() {
let items = {};
// has(key)方法
this.has = function(val) {
return items.hasOwnProperty(val);
};
}
複製代碼
實現了has方法後,咱們能夠來判斷字典中是否包含該屬性了,繼續來實現其餘方法
// set(key, val)方法
// set相同key時,後面聲明的會覆蓋前面
// 如: new Map().set({}, 'a')
this.set = function(key, val) {
items[key] = val;
};
// get(key)方法
this.get = function(key) {
// 判斷是否有key,若是有的話直接返回對應的值
// 若是讀取一個未知的鍵,則返回undefined
return this.has(key) ? items[key] : undefined;
};
複製代碼
set和get方法寫好了,再接着搞delete和clear方法,不廢話,看
// delete(key)方法
this.delete = function(key) {
if (this.has(key)) { // 若是有key值
delete items[key]; // 直接刪掉items上對應的屬性
this.size--; // 讓size總數減1
return true;
}
return false;
};
// clear()方法
this.clear = function() {
items = {};
this.size = 0;
};
複製代碼
上面把屬性和操做方法都分別完成了,還剩下最後的遍歷方法了,繼續寫下去,堅持到底就是勝利,各位看官也不容易了,加油加油!!!
// keys()方法
this.keys = function() {
return Object.keys(items);
};
// values()方法
this.values = function() {
return Object.values(items);
};
// forEach(fn, context)方法
this.forEach = function(fn, context = this) {
for (let i = 0; i < this.size; i++) {
let key = Object.keys(items)[i];
let value = Object.values(items)[i];
fn.call(context, value, key, items);
}
};
複製代碼
Now終於完成了Map類的實現,我給你們貼一下完整代碼和測試用例,供你們空閒時間來研究分析分析
function Map() {
let items = {};
this.size = 0;
// 操做方法
// has方法
this.has = function(val) {
return items.hasOwnProperty(val);
};
// set(key, val)方法
this.set = function(key, val) {
items[key] = val;
this.size++;
};
// get(key)方法
this.get = function(key) {
return this.has(key) ? items[key] : undefined;
};
// delete(key)方法
this.delete = function(key) {
if (this.has(key)) {
delete items[key];
this.size--;
return true;
}
return false;
};
// clear()方法
this.clear = function() {
items = {};
this.size = 0;
};
// 遍歷方法
// keys()方法
this.keys = function() {
return Object.keys(items);
};
// values()方法
this.values = function() {
return Object.values(items);
};
// forEach(fn, context)方法
this.forEach = function(fn, context = this) {
for (let i = 0; i < this.size; i++) {
let key = Object.keys(items)[i];
let value = Object.values(items)[i];
fn.call(context, value, key, items);
}
};
}
module.exports = Map;
複製代碼
再來看看下面的測試栗子
// map.js
// 使用Map類
const Map = require('./Map.js');
let m = new Map();
m.set('Jay', 'Jay的Chou');
m.set(true, '真的');
console.log(m.has('Chou')); // false
console.log(m.size); // 2
console.log(m.keys()); // [ 'Jay', 'true' ]
console.log(m.values()); // [ 'Jay的Chou', '真的' ]
console.log(m.get('jay')); // undefined
m.delete(true);
console.log(m.keys()); // [ 'Jay' ]
console.log(m.values()); // [ 'Jay的Chou' ]
複製代碼
最後的戰役,作個非正式的總結吧
根據集合和字典這兩種數據結構實現了相似ES6的Set和Map數據結構。
但其實完成的還不完善,還有一些方法須要你們一塊兒再去思考一下
這裏我只是用集合和字典來給你們牽個頭,也但願你們看完後能夠集思廣益的去實現一下
OK,那麼感謝你們的觀看了,前面都是廢話,這纔是真心的,哈哈