There are 2 design choices that really invite criticism. First, not protecting the implementation of features that are essential for the data-structure’s correct/predictable functioning by exposing every detail/property into public. Secondly not using an array internally for data storage, thus being forced to reimplement all the logic related to a queue's head, tail and peek features.
An alternative implementation, in its most basic form, which does not feature the criticized flaws, might look similar to the following one …
class Queue {
#list = [];
constructor(...args) {
this.#list.push(...args);
}
// `first` or `front` or `peek`.
first() {
return this.#list.at(0);
}
// `last` or `back`.
last() {
return this.#list.at(-1);
}
size() {
return this.#list.length;
}
enqueue(value) {
return this.#list.push(value);
}
dequeue() {
return this.#list.shift();
}
}
const queue = new Queue;
console.log(
'queue.size() ...', queue.size(), // logs ... 0.
);
console.log(
"queue.enqueue('quick') ...", // logs ... 1.
queue.enqueue('quick'),
);
console.log(
"queue.enqueue('brown') ...", // logs ... 2.
queue.enqueue('brown'),
);
console.log(
"queue.enqueue('fox') ...", // logs ... 3.
queue.enqueue('fox'),
);
console.log(
'queue.first() ...', queue.first(), // logs ... 'quick'.
);
console.log(
'queue.last() ...', queue.last(), // logs ... 'fox'.
);
console.log(
'queue.size() ...', queue.size(), // logs ... 3.
);
console.log(
"queue.dequeue() ...", queue.dequeue(), // logs ... 'quick'.
);
console.log(
"queue.dequeue() ...", queue.dequeue(), // logs ... 'brown'.
);
console.log(
"queue.dequeue() ...", queue.dequeue(), // logs ... 'fox'.
);
console.log(
"queue.dequeue() ...", queue.dequeue(), // logs ... undefined.
);
And since JavaScript is an event driven language too, one easily can iterate the above example code into its final version of a Queue
class which does dispatch actions like enqueue
and dequeue
, as well as changes to its inner state like an empty
queue …
function dispatch(target, type, ...valueRest) {
const options = valueRest.length && {
detail: {
value: valueRest.at(0),
}
} || null;
target.dispatchEvent(
new CustomEvent(type, options)
);
}
class Queue extends EventTarget {
#list = [];
constructor(...args) {
super();
this.#list.push(...args);
}
// `first` or `front` or `peek`.
first() {
return this.#list.at(0);
}
// `last` or `back`.
last() {
return this.#list.at(-1);
}
size() {
return this.#list.length;
}
enqueue(...args) {
let size = this.size();
if (args.length !== 0) {
const value = args.at(0);
size = this.#list.push(value);
dispatch(this, 'enqueue', value);
// setTimeout(dispatch, 0, this, 'enqueue', value);
} else if (size === 0) {
dispatch(this, 'empty');
// setTimeout(dispatch, 0, this, 'empty');
}
return size;
}
dequeue() {
const recentSize = this.size();
const value = this.#list.shift();
if (recentSize >= 1) {
dispatch(this, 'dequeue', value);
// setTimeout(dispatch, 0, this, 'dequeue', value);
}
if (recentSize <= 1) {
dispatch(this, 'empty');
// setTimeout(dispatch, 0, this, 'empty');
}
return value;
}
}
const queue = new Queue;
queue.addEventListener('enqueue', evt => console.log({ evt }));
queue.addEventListener('dequeue', evt => console.log({ evt }));
queue.addEventListener('empty', evt => console.log({ evt }));
console.log(
'queue.size() ...', queue.size(), // logs ... 0.
);
queue.enqueue(); // does log a custom 'empty' event.
queue.enqueue('quick'); // does log a custom 'enqueue' event.
queue.enqueue('brown'); // does log a custom 'enqueue' event.
queue.enqueue('fox'); // does log a custom 'enqueue' event.
console.log(
'queue.size() ...', queue.size(), // logs ... 3.
);
queue.dequeue(); // does log a custom 'dequeue' event.
queue.dequeue(); // does log a custom 'dequeue' event.
queue.dequeue(); // does log both custom events, 'dequeue' and 'empty'.
queue.dequeue(); // does log a single custom 'empty' event.