As a preface for my question, I am very new to JavaScript and come from a strongly-typed language background (e.g. Java and C++). Although I have prior experience with JavaScript, I am not entirely familiar with its underlying mechanisms and struggle to engage with the problems posed by dynamic typing. This has not deterred me from trying very hard to not only appreciate the language, but learn to use it effectively.
With that out of the way, I tried to create a Doubly-Linked List using Node.js
as a practice problem; I am quite familiar with the data structure, so I figured this would be a safe starting point for me. I am also trying to only use ES Modules to construct individual Nodes of the Doubly-Linked List. This self-imposed constraint was intended to push me outside of my comfort zone with classes and utilize the most recent JS innovations available.
However, it seems my choice to use ES Modules is not panning out. In summary, the return value of the functions getRightNode()
and getLeftNode()
do not have a return value that JS can identify properly. I say this because my efforts to call the function getNodeItem()
does not work and throws the following error message:
console.log(`right = ${ temp.getNodeItem() }`);
^
TypeError: Cannot read properties of undefined (reading 'getNodeItem')
I believe this problem is a direct result of me creating an object without using a classified type. This assertion could be wrong, but it appears to me that the return values of getLeftNode()
and getRightNode()
cannot be properly tied back to Node objects created by NodeModuleFactory
, hence the program’s inability to call the unknown object’s function and the error is thrown.
My questions are as follows:
- Is there a way to fix this and allow the program to call the
getNodeItem()
function from the returned value ofgetRightNode()
andgetLeftNode()
? - Am I using ES Modules incorrectly/should I simply be using classes for this?
Any insight about this would be greatly appreciated, as I am unsure how to proceed at this stage and feel as though I am missing something.
// File: NodeModuleFactory.js
"use strict"; // <- Requires us to use the best practices for JavaScript coding.
/**
* Determines whether the parameter passed into the function is a Node by checking
* to see if it has certain functions.
* @param check A variable that we will verify whether it is or is not a Node of some
* kind.
* @returns {boolean} Where a false value means the parameter is not a Node, and true
* means it is a Node.
*/
function isNode (check) {
return (typeof check === "object" &&
typeof check.getLeftNode() === "object" &&
typeof check.getRightNode() === "object" &&
typeof check.getLeftNode === "function" &&
typeof check.getRightNode === "function" &&
typeof check.getNodeItem === "function" &&
typeof check.setLeftNode === "function" &&
typeof check.setRightNode === "function");
}
/**
* Allows for the creation of NodeModule objects.
* @param leftNode This should either be a NodeModule or undefined and will be treated
* as the NodeModule to the left of the newly created NodeModule object.
* @param rightNode This should either be a NodeModule or undefined and will be treated
* as the NodeModule to the right of the newly created NodeModule object.
* @param item This is the item being stored within the newly created NodeModule
* object.
* @returns {undefined|{getRightNode(): NodeModule, setLeftNode(*): void,
* getNodeItem(): *, setRightNode(NodeModule): void, getLeftNode(): NodeModule}}
* A NodeModule object that can be accessed and modified within a doubly-linked
* list data structure. All NodeModule objects will have an immutable item property.
*/
export function create(leftNode, rightNode, item)
{
// Check to see if either leftNode or rightNode are actually a NodeModule object
// or set to undefined. If neither of these are the case, then return undefined.
if (!(leftNode === undefined || isNode(leftNode)) ||
!(rightNode === undefined || isNode(rightNode)))
return undefined;
// 'Private' data members of the NodeModule object.
let left = leftNode;
let right = rightNode;
return {
/**
* Obtains the NodeModule object directly to the left of this NodeModule
* object.
* @returns {NodeModule} Should be a NodeModule object.
*/
getLeftNode() {
return left;
},
/**
* Obtains the NodeModule object directly to the right of this NodeModule
* object.
* @returns {NodeModule} Should be a NodeModule object.
*/
getRightNode() {
return right;
},
/**
* Obtains this NodeModule object's item that is contained within.
* @returns {*} Could be any item stored within the LinkedList structure.
*/
getNodeItem() {
return item;
},
/**
* Mutates the left data member if newLeft can be determined to be a
* NodeModule object or undefined.
* @param newLeft The object or undefined value that should replace the
* current left data member.
* @returns {boolean} True means that the reassignment was successful, while
* false means that the reassignment was unsuccessful.
*/
setLeftNode(newLeft) {
if ((newLeft === undefined || isNode(newLeft))) {
left = newLeft;
return true;
}
return false;
},
/**
* Mutates the right data member if newLeft can be determined to be a
* NodeModule object or undefined.
* @param newRight The object or undefined value that should replace the
* current right data member.
* @returns {boolean} True means that the reassignment was successful, while
* false means that the reassignment was unsuccessful.
*/
setRightNode(newRight) {
if ((newRight === undefined || isNode(newRight))) {
right = newRight;
return true;
}
return false;
}
};
}
// File: main.js
"use strict"; // <- requires strict adherence to JavaScript best practices.
import { create as newNode } from "./NodeModuleFactory.js";
let head = newNode(undefined, undefined, 0);
let tail = newNode(head, undefined, 1);
console.log(head.setRightNode(tail));
console.log("head");
console.log(`cur = ${ head.getNodeItem() }`);
console.log(`right = ${ head.getRightNode().getNodeItem() }`);
console.log("ntail");
console.log(`left = ${ tail.getLeftNode().getNodeItem() }`);
console.log(`cur = ${ tail.getNodeItem() }`);