From e3db67017b71777f50cb6bab75ccc1cf1f3b5e67 Mon Sep 17 00:00:00 2001 From: isaacs Date: Tue, 24 Nov 2015 10:57:40 -0800 Subject: [PATCH] Use a Map on newer systems, polyfill for older JS --- lib/lru-cache.js | 64 ++++++++++++++++++++++++++++++++++++------------ test/basic.js | 5 ++++ 2 files changed, 53 insertions(+), 16 deletions(-) diff --git a/lib/lru-cache.js b/lib/lru-cache.js index 5ee6548f..99f9df2f 100644 --- a/lib/lru-cache.js +++ b/lib/lru-cache.js @@ -7,6 +7,41 @@ if (typeof module === 'object' && module.exports) { this.LRUCache = LRUCache } +var hasOwnProperty = Object.prototype.hasOwnProperty + +var MyMap + +if (typeof Map === 'function') + MyMap = Map +else { + MyMap = function MyMap () { + this._data = Object.create(null) + } + + MyMap.prototype.forEach = function (fn, thisp) { + thisp = thisp || this + Object.keys(this._data).forEach(function (k) { + fn.call(thisp, this._data[k], k) + }, this) + } + + MyMap.prototype.has = function (k) { + return hasOwnProperty.call(this._data, k) + } + + MyMap.prototype.get = function (k) { + return this._data[k] + } + + MyMap.prototype.set = function (k, v) { + this._data[k] = v + } + + MyMap.prototype.delete = function (k) { + delete this._data[k] + } +} + function naiveLength () { return 1 } function typeCheckKey(key) { @@ -56,16 +91,16 @@ Object.defineProperty(LRUCache.prototype, "lengthCalculator", if (typeof lC !== "function") { this._lengthCalculator = naiveLength this._length = this._itemCount - for (var value of this._cache.values()) { + this._cache.forEach(function (value, key) { value.length = 1 - } + }) } else { this._lengthCalculator = lC this._length = 0 - for (var value of this._cache.values()) { + this._cache.forEach(function (value, key) { value.length = this._lengthCalculator(value.value) this._length += value.length - } + }) } if (this._length > this._max) trim(this) @@ -125,13 +160,13 @@ LRUCache.prototype.values = function () { LRUCache.prototype.reset = function () { if (this._dispose && this._cache) { - for (var entry of this._cache) { - this._dispose(entry[0], entry[1].value) - } + this._cache.forEach(function (entry, key) { + this._dispose(key, entry.value) + }, this) } - this._cache = new Map() // hash of items by key - this._lruList = new Map() // list of items in order of use recency + this._cache = new MyMap() // hash of items by key + this._lruList = new MyMap() // list of items in order of use recency this._mru = 0 // most recently used this._lru = 0 // least recently used this._length = 0 // number of items in the list @@ -163,13 +198,11 @@ LRUCache.prototype.dumpLru = function () { } LRUCache.prototype.set = function (key, value, maxAge) { - // Map allows any type of `key` (objects, number, etc). For backwards - // compatibility, coerce to a string. - key = String(key) - maxAge = maxAge || this._maxAge typeCheckKey(key) + key = String(key) + var now = maxAge ? Date.now() : 0 var len = this._lengthCalculator(value) @@ -250,6 +283,7 @@ LRUCache.prototype.pop = function () { LRUCache.prototype.del = function (key) { if (typeof key !== 'string' && typeof key !== 'number') return + key = String(key) del(this, this._cache.get(key)) } @@ -276,10 +310,8 @@ LRUCache.prototype.load = function (arr) { function get (self, key, doUse) { typeCheckKey(key) - // Map allows any type of `key` (objects, number, etc). For backwards - // compatibility, coerce to a string. - key = String(key) + key = String(key) var hit = self._cache.get(key) if (hit) { if (isStale(self, hit)) { diff --git a/test/basic.js b/test/basic.js index eb63fee9..e59b557e 100644 --- a/test/basic.js +++ b/test/basic.js @@ -423,6 +423,11 @@ test("peek only accepts strings and numbers as keys", function(t) { t.equal(cache.peek("key"), "value") t.equal(cache.peek(123), 456) + + t.equal(cache.peek({ + toString: function() { return "key" } + }), undefined) + t.end() })