I wrote this for a CodeWars challenge while trying to learn ECMAScript and would really like to have some advice on how it could be improved.
What I don't like about this code myself, but am unsure on how to improve:
- I didn't yet found a way to have a class in ECMAScript that mixes public and private methods in a way, where the public methods (and only them) can still access private methods and vars.
- (Partly caused by the point above) I think the code is way too verbose and bloated. Too much of it is wasted on language specific implementation details. There must be a smarter way to do this - I feel like I could write the same functionality in PHP using 10% of the lines I needed now.
function LRUCache(capacity, init) {
this._capacity = capacity;
this._size = 0;
this._accessMap = [];
this._items = [];
this._frozenMethods = ['cache', 'delete'];
this._functionalProps = ['size', 'capacity'];
this.cache = function(key, value) {
if( this._accessMap.indexOf(key) == -1 ) {
this._addNewItem(key);
}
this._items[key] = this[key] = value;
this._updateAccessMap(key);
return this;
},
this.delete = function(key) {
if(this._frozenMethods.indexOf(key) !== -1 ||
this._functionalProps.indexOf(key) !== -1) {
return false;
}
if( this._accessMap.indexOf(key) === -1 ) {
return true;
}
delete this[key];
delete this._items[key];
this._size--;
this._updateAccessMap(key, true);
return true;
},
this._removeOldest = function() {
var key = this._accessMap[0];
this.delete(key);
},
this._updateAccessMap = function(key, remove) {
index = this._accessMap.indexOf(key);
if( index !== -1) {
this._accessMap.splice(index, 1);
}
if(!remove) {
this._accessMap.push(key);
}
},
this._addNewItem = function(key) {
this._size++;
if(this._size > this._capacity) {
this._removeOldest();
}
Object.defineProperty(this, key, {
get: function() {
this._updateAccessMap(key);
return this._items[key];
},
set: function(v) {
this._items[key] = v;
this._updateAccessMap(key);
return this;
},
enumerable: true,
configurable: true
});
};
var obj = this;
this._frozenMethods.map( function(v) {
Object.defineProperty(obj, v, {
enumerable: false,
writable: false,
configurable: false
});
});
['_items', '_capacity', '_accessMap', '_frozenMethods','_size', '_functionalProps',
'_removeOldest','_updateAccessMap','_addNewItem'].map( function(v) {
Object.defineProperty(obj, v, {
enumerable: false,
configurable: true
});
});
Object.defineProperty(this, 'capacity', {
set: function(v) {
while(this.size > v) {
this._removeOldest();
}
this._capacity = v;
},
get: function() {
return this._capacity;
},
configurable: false
});
Object.defineProperty(this, 'size', {
set: function(v) {
return false;
},
get: function() {
return this._size;
},
configurable: false
});
if(init) {
for(var attr in init) {
if( !init.hasOwnProperty(attr) ) {
continue;
}
this.cache(attr, init[attr]);
}
}
}