You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaScript模块对象识别失败致TypeError:无法读取undefined的getNodeItem属性

问题

我有Java、C++强类型语言背景,刚深入学习JavaScript,对动态类型机制不熟悉。尝试用Node.js结合ES模块实现双向链表时遇到问题:调用getRightNode()和getLeftNode()的返回值后,无法调用getNodeItem(),报错TypeError: Cannot read properties of undefined (reading 'getNodeItem')。我推测是没使用类创建对象导致无法识别Node类型。想问两个问题:

  1. 如何修复该问题,让getRightNode()和getLeftNode()的返回值能调用getNodeItem()?
  2. 我是否错误使用了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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 17:20:52