JavaScript模块对象识别失败致TypeError:无法读取undefined的getNodeItem属性
问题
我有Java、C++强类型语言背景,刚深入学习JavaScript,对动态类型机制不熟悉。尝试用Node.js结合ES模块实现双向链表时遇到问题:调用getRightNode()和getLeftNode()的返回值后,无法调用getNodeItem(),报错TypeError: Cannot read properties of undefined (reading 'getNodeItem')。我推测是没使用类创建对象导致无法识别Node类型。想问两个问题:
- 如何修复该问题,让
getRightNode()和getLeftNode()的返回值能调用getNodeItem()? - 我是否错误使用了ES模块,是否应该改用类实现?
代码文件
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; } }; }
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() }`);
解决方案
1. 修复调用报错的问题
问题根源在isNode函数的判断逻辑:
- 原函数先调用
check.getLeftNode()和check.getRightNode(),并要求它们的返回值类型必须是object,但链表首尾节点的左右指针本身就是undefined,导致合法节点也会被判定为非Node。 - 同时未排除
check为null的情况(typeof null也是object),会引发额外错误。
修改isNode函数如下:
function isNode(check) { return (typeof check === "object" && check !== null && // 先判断方法存在,再验证返回值允许为undefined或object typeof check.getLeftNode === "function" && typeof check.getRightNode === "function" && typeof check.getNodeItem === "function" && typeof check.setLeftNode === "function" && typeof check.setRightNode === "function" && (check.getLeftNode() === undefined || typeof check.getLeftNode() === "object") && (check.getRightNode() === undefined || typeof check.getRightNode() === "object")); }
修改后,创建tail时,isNode(head)会返回true,create函数能正常返回节点对象,tail不再是undefined,后续调用head.getRightNode().getNodeItem()就能正常获取值。
2. ES模块使用与类实现的选择
- ES模块使用正确:你的
export和import写法符合Node.js ES模块规范(需要确保package.json中设置"type": "module"),没有问题。 - 是否改用类实现:两种方式都可行,各有优劣:
- 你当前的工厂函数写法,通过闭包实现了真正的私有变量(
left、right、item外部无法直接访问),封装性更好。 - 类写法更贴近Java/C++的习惯,代码结构更直观,但如果需要私有字段,需要使用ES2022+的
#前缀(如#left),否则外部仍能访问内部属性。
- 你当前的工厂函数写法,通过闭包实现了真正的私有变量(
可以根据自己的习惯和需求选择:如果更熟悉类的结构,或者不需要强私有性,类写法会更易读;如果需要严格的私有变量,工厂函数是更好的选择。
内容的提问来源于stack exchange,提问作者KeyboardClone
相关产品推荐
相关产品推荐

