I'm looking for feedback on my singly linked list implementation in JavaScript. Please let me know if you have any suggestions on coding style, documentation, bug fixes, etc.
UML / code overview
LinkedList _head - The first element in the list _size - The number of elements in the list getSize() Returns the number of elements in the list get(index) Returns the node at index Throws a ReferenceError if index is not specified Throws a RangeError if index is out of range getFirst() Returns the first element in the list Calls and returns this.get(0) getLast() Returns the last element in the list Calls and returns this.get(this._size - 1) add(node, index) Inserts the given node at the specified index Throws a ReferenceError if node is not passed Throws a TypeError if node is not an instance of ListNode addFirst(node) Inserts the given node at the front of the list Calls and returns add(node, 0) addLast(node) Appends the given node at the end of the list Calls and returns add(node, this._size - 1) remove(index) Removes and returns the node at the specified index Throws a ReferenceError if index is undefined Throws a RangeError if index is not between 0 and this._size removeFirst() Removes and returns the first node in the list Calls and returns remove(0) removeLast() Removes and returns the last node in the list Calls and returns remove(0) clear() Removes all elements in the list set(index, node) Replaces the node at index with the given node Calls this.remove(index) then calls and returns this.add(node, index) indexOf(value) Returns -1 or the first index value is found at Throws a ReferenceError if value is undefined lastIndexOf(value) Returns -1 or the last index value is found at Throws a ReferenceError if value is undefined contains(value) Returns true/false if the value is/not in the list ListNode _value - The value this node holds _next - The next node in the list getValue() setValue(value) getNext() setNext(node)
Code
I've left out the ListNode
class because there's nothing more to it than the UML above.
class LinkedList { // Initializes the list by setting the head to null, // and the size to 0 constructor() { this._head = null; this._size = 0; } // Returns the number of nodes in the list getSize() { return this._size; } // Returns the head of the list getFirst() { return this._head; } // Returns the tail of the list getLast() { return this.get(this._size - 1); } // Returns the node at the specified index get(index) { // If the index parameter was not specified if (index === undefined) { throw new ReferenceError('no index was specified', 'LinkedList.js'); } // Make sure the index is in range if (index < 0 || index >= this._size) { const msg = `Index (${index}) out of range; List size (${this._size})`; throw new RangeError(msg, 'LinkedList.js'); } let current = this._head; for (let i = 0; i < index; i++) { current = current.getNext(); } return current; } // Inserts the given node at the specified index add(node, index) { // If the node parameter was not specified if (node === undefined) { throw new ReferenceError('no node was specified', 'LinkedList.js'); } // Make sure node is a ListNode if (!(node instanceof ListNode)) { const msg = "add(node, index): node must be of type ListNode"; throw new TypeError(msg, 'LinkedList.js'); } // The index parameter is optional. If not defined, add node to the end if (index === undefined) { index = this._size; } // Make sure the index is in range if (index < 0 || index > this._size) { const msg = `Index (${index}) out of range; List size (${this._size})`; throw new RangeError(msg, 'LinkedList.js'); } // If inserting the node at the beginning of the list if (index === 0) { // Set the node's next to point to the current head node.setNext(this._head); // Replace the current head with the new node this._head = node; this._size++; return node; } else { // The node just before the insertion index let previous = this.get(index - 1); // The node at the insertion index let next = previous.getNext(); // Set the node at the insertion index previous.setNext(node); node.setNext(next); this._size++; return node; } } // Inserts the given node at the beginning of the list addFirst(node) { return this.add(node, 0); } // Appends the given node at the beginning of the list addLast(node) { return this.add(node, this._size); } // Removes and returns the node at the specified index remove(index) { // If the index parameter was not specified if (index === undefined) { throw new ReferenceError('no index was specified', 'LinkedList.js'); } // Make sure the index is in range if (index < 0 || index > this._size) { const msg = `Index (${index}) out of range; List size (${this._size})`; throw new RangeError(msg, 'LinkedList.js'); } // If removing the head of the list if (index === 0) { const removed = this._head; this._head = this._head.getNext(); this._size--; return removed; } else { // The node at the index before the one to be removed let previous = this.get(index - 1); // The node to be removed let removed = previous.getNext(); // Removes the node at index by setting the next pointer of the node at // index-1 to the node at index+1, skipping the node at index previous.setNext(removed.getNext()); this._size--; return removed; } } // Removes and returns the head of the list removeFirst() { return this.remove(0); } // Removes and returns the last node in the list removeLast() { return this.remove(this._size - 1); } // Empties the list by setting the head to null and sets the size to 0 clear() { this._head = null; this._size = 0; } // Replaces the node at the specified index with the given node set(index, node) { // Remove the node currently at index this.remove(index); // Inserts the new node at the specified index return this.add(node, index); } // Returns the index that the value is found at, -1 if the value is not found indexOf(value) { // If the index parameter was not specified if (value === undefined) { throw new ReferenceError('no value was specified', 'LinkedList.js'); } // If the list is empty, return -1 indicating the value wasn't found if (this._head == null) { return -1; } let current = this._head; let index = 0; while (current.getNext() != null) { if (current.getValue() === value) { return index; } current = current.getNext(); index++; } return -1; } // Returns the last index the value is found at, -1 if it is not found lastIndexOf(value) { // If the index parameter was not specified if (value === undefined) { throw new ReferenceError('no value was specified', 'LinkedList.js'); } // If the list is empty, return -1 indicating the value wasn't found if (this._head == null) { return -1; } let current = this._head; let lastMatch = -1; let index = 0; while (current != null) { // If a match is found, update the lastMatch to the current index if (current.getValue() === value) { lastMatch = index; } current = current.getNext(); index++; } return lastMatch; } // Returns true if the value is in the list, false otherwise contains(value) { // Returns true if indexOf() finds the value, false if it does not return (this.indexOf(value) !== -1); } }