
pseudomap項(xiàng)目是一個ES6之前使用Map數(shù)據(jù)結(jié)構(gòu)的一個工具庫,map的特點(diǎn)在于拓展了對象的鍵只能是字符串的短板,在ES6之前很容易想到使用一個構(gòu)造函數(shù)來實(shí)現(xiàn),同時(shí)對于每一個鍵值關(guān)系使用一個新的對象存儲,this.key=key;this.value=value的形式。適合JavaScript初學(xué)者學(xué)習(xí)構(gòu)造函數(shù)的使用,同時(shí)大神的代碼質(zhì)量還是非常值得學(xué)習(xí)的,大神終究是大神?。。?/p>
var hasOwnProperty = Object.prototype.hasOwnProperty
module.exports = PseudoMap
function PseudoMap (set) {
// 檢測this的指向來判斷該函數(shù)是否被當(dāng)做構(gòu)造函數(shù)運(yùn)行,使用new實(shí)例化
// 構(gòu)造函數(shù)時(shí)將改變this的指向?yàn)樵搶ο螅苯舆\(yùn)行函數(shù),this則是指全局對象
if (!(this instanceof PseudoMap)) // whyyyyyyy
throw new TypeError("Constructor PseudoMap requires 'new'")
this.clear()
// 設(shè)置傳遞的參數(shù)
if (set) {
if ((set instanceof PseudoMap) ||
(typeof Map === 'function' && set instanceof Map))
set.forEach(function (value, key) {
this.set(key, value)
}, this)
else if (Array.isArray(set))
set.forEach(function (kv) {
this.set(kv[0], kv[1])
}, this)
else
throw new TypeError('invalid argument')
}
}
PseudoMap.prototype.forEach = function (fn, thisp) {
thisp = thisp || this
Object.keys(this._data).forEach(function (k) {
if (k !== 'size')
fn.call(thisp, this._data[k].value, this._data[k].key)
}, this)
}
PseudoMap.prototype.has = function (k) {
return !!find(this._data, k)
}
PseudoMap.prototype.get = function (k) {
var res = find(this._data, k)
// 利用邏輯與的執(zhí)行特點(diǎn),不存在時(shí)直接返回undefined,這樣就不需要在find方法中去判斷了,妙
return res && res.value
}
// 存儲
PseudoMap.prototype.set = function (k, v) {
set(this._data, k, v)
}
// 刪除
PseudoMap.prototype.delete = function (k) {
var res = find(this._data, k)
if (res) {
delete this._data[res._index]
this._data.size--
}
}
PseudoMap.prototype.clear = function () {
// 創(chuàng)建一個空對象
var data = Object.create(null)
data.size = 0
// 重新設(shè)置構(gòu)造函數(shù)的_data屬性,不可寫,不可枚舉防止其被篡改
Object.defineProperty(this, '_data', {
value: data,
enumerable: false,
configurable: true,
writable: false
})
}
// 設(shè)置size的get方法返回this._data.size
Object.defineProperty(PseudoMap.prototype, 'size', {
get: function () {
return this._data.size
},
set: function (n) {},
enumerable: true,
configurable: true
})
PseudoMap.prototype.values =
PseudoMap.prototype.keys =
PseudoMap.prototype.entries = function () {
throw new Error('iterators are not implemented in this version')
}
// 兩者相等或者兩個都是NaN時(shí)返回true,注意邏輯與的優(yōu)先級大于邏輯或
// Either identical, or both NaN
function same (a, b) {
return a === b || a !== a && b !== b
}
// 用來儲存鍵值信息的對象
function Entry (k, v, i) {
this.key = k
this.value = v
this._index = i
}
function find (data, k) {
for (var i = 0, s = '_' + k, key = s;
hasOwnProperty.call(data, key);
key = s + i++) {
if (same(data[key].key, k))
return data[key]
}
}
function set (data, k, v) {
// 此時(shí)則是重新設(shè)置值
for (var i = 0, s = '_' + k, key = s;
hasOwnProperty.call(data, key);
key = s + i++) {
if (same(data[key].key, k)) {
data[key].value = v
return
}
}
// size自增
data.size++
// 將鍵值關(guān)系作為一個Entry對象儲存,這樣就避免了傳統(tǒng)的對象鍵只能是字符串的短板
data[key] = new Entry(k, v, key)
}