Reputation: 357
// Garbage collection friendly list.
class GCFList extends Array {
size;
constructor(initSize = 0) {
super(initSize);
this.size = initSize;
}
push(content){
this[this.size] = content;
this.size++;
}
pop(){
this.size--;
let returnContent = this[this.size];
this[this.size] = null;
return returnContent;
}
get length(){
return this.size;
}
set length(newLength){
}
}
var l = new GCFList();
l.push(2);
l.pop();
console.log(l.length);
console.log("Expecting 0, but getting 1");
I'm making a garbage collection friendly array list. I want to use it as a normal array. When I try to override the length getter method, it seems like it is still accessing the parents (Array) length. How do I make it so when I call l.length, I get the size of l?
Upvotes: 1
Views: 99
Reputation: 920
(The solution below is indirectly connected to the question but at the same time it was too long to be posted in comment.)
As you experienced, extending Array might be problematic. It’s probably the best collection JavaScript has to offer but on its own. What I want to suggest is following:
(Yes, I believe that structure you’re looking for in your question is stack.)
class Stack {
constructor(size) {
this.size = size;
this.head = -1;
this.stack = Array.from({ length: size }, () => undefined);
}
push(item) {
if (this.head + 1 == this.size) {
// if you prefer, this might silently fail but I chose to be explicit
throw new Error('Stack full!');
}
this.head += 1;
this.stack[this.head] = value;
return this; // so it can be chained and `this.push(1).push(2)` will be possible
}
pop() {
if (this.head == -1) {
// if you prefer, this might silently fail but I chose to be explicit
throw new Error('Stack empty!');
}
const popped = this.stack[this.head];
// this is theoretically optional but in case of objects we’ll get rid of reference,
// hence allowing for garbage collection
this.stack[this.head] = undefined;
this.head -= 1;
return popped;
}
get length() {
// I put this here as it was in your example
// but either having `lenght` property or reading from `string` is actually enough
return this.size;
}
set length(size) {
if (size > this.size) {
for (let i = this.size; i < size; i++) {
this.stack.push(undefined);
}
} else if (size < this.size) {
if (this.head > size) {
this.head = size - 1; // set it at the end of shorter stack if head would be oustide
}
for (let i = this.size; i > size; i--) {
this.stack.pop();
}
}
}
}
This gives you “an array” with fixed length and will fail if you try to extend it. I read somewhere that for purposes of games arrays that don’t change their length work better. And in any case, you did your profiling already. Also, because of that I’m not gonna suggest using immutable structure, as this would be heavier on memory.
Possible method you might need also is peek
that allows to see the current value without popping it.
This is proof of concept that I just wrote so it might need some tweaking, if you decided to go with it, but this is what I had in mind.
Because this means to be fast, I dropped some overly defensive measures, like checking if size
sent to constructor is a number. I assume it will be for more internal use so you will take care of it.
Upvotes: 0
Reputation: 19569
Aside from the problem that you can't really be much more efficient then JS Array (they're sparse etc), you're extending Array, and array's length
property is not configurable:
Object.getOwnPropertyDescriptor(l, 'length')
// {value: 1, writable: true, enumerable: false, configurable: false}
Means, you can't change that property.
Upvotes: 2
Reputation: 664395
You cannot overwrite the .length
behaviour of an array. It is not a getter/setter (even though it behaves like one), and it is not inherited from Array.prototype
. Each array instance has its own .length
data property, which shadows your getter/setter on GCFList.prototype
.
Upvotes: 4