在嵌套函数上调用方法时出现 TypeError

TypeError when calling methods on nested functions

我正在尝试使用两个 类(节点和图形)创建图形。我能够构建一个基本图形。但是,当调用 addChild 方法将子节点添加到嵌套节点的子 属性 时,我收到以下类型错误。谁能建议如何修复此类型错误?

类型错误:graph.root.children[0].addChild 不是函数

class Node {

    name = '';
    children = [];

    constructor() {}

    createNode(name, childNames) {
        this.name = name;
        childNames.forEach(childName => {
            this.children.push({name: childName, children: []});
        });
        return { name: this.name,
            children: this.children};
    } // createNode
}

class Graph {
    root = new Node();
    constructor(node) {
        const { name, children } = node;
        this.root = {
            name,
            children
        };
    }
    addChild(node) {
        console.log("Graph", JSON.stringify(node));
        this.root.children.push(node);
    }
}
// Add the three child nodes under node A's children property (B (this.root.children[0]), C(this.root.children[1]), D(this.root.children[2]))
const rootNode = new Node();
rootNode.createNode('A', ['B', 'C', 'D']);
const graph = new Graph(rootNode);
// rootNode = new Node().createNode();
// add to bNode this.root.children[0]
const eNode = new Node();
eNode.createNode('E', []);

const fNode = new Node();
fNode.createNode('F', []);

console.log(`Graph: ${JSON.stringify(graph)}`);

console.log(`graph: ${JSON.stringify(graph.root.children[0])}`);
graph.root.children[0].addChild(eNode);

第一件事是您在 Node 对象上调用了 addChild(), 第二个是您正在子节点

中搜索根 属性

这是我的快速解决方案:

addChild(node) {
    console.log("Graph", JSON.stringify(node));
    this.children.push(node);
}

graph.addChild.call(graph.root.children[0], eNode); console.log(graph: ${JSON.stringify(graph.root.children[0])});

根据反馈,所有对象都需要声明为节点对象,以便在嵌套对象上调用 addChild 方法。请参阅下面的新 class:

class Node {
    name = '';
    children = [];

    constructor(name, childNames) {
        this.name = name;
        this.children = childNames;
    }

    createNodeWithChildren(name, childNames) {
        this.name = name;
        const childArray = [];
        this.children = childNames.map(childName => {
            return { name: childName, children: [] };
        });
        console.log(`${this.name}: ${JSON.stringify(this.children)}`);
        return new Node( this.name, this.children);
    } // createNode

    addChild({ name, children }) {
        const newNode = new Node(name, children);
        console.log("NODE", JSON.stringify(newNode));
        this.children.push(newNode);
    }
}

// Graph is just a handle to a bunch of nodes.  Add the three child nodes under node A's children property (B (this.root.children[0]), C(this.root.children[1]), D(this.root.children[2]))
const graph = new Node('A', []);
const bNode = new Node('B', []);
const cNode = new Node('C', []);
const dNode = new Node('D', []);
graph.addChild(bNode);
graph.addChild(cNode);
graph.addChild(dNode);

/*************************************/
// add to bNode this.root.children[0]
let eNode = new Node('E', []);
eNode.createNodeWithChildren('E', ['Q', 'R']);
console.log(`new Enode:`, JSON.stringify(eNode));

let fNode = new Node('F', []);
graph.children[0].addChild(eNode);
graph.children[0].addChild(fNode);
console.log(`Graph: ${JSON.stringify(graph)}`);

抱歉,如果它太难读了,但我对你的 class 发表了评论,我认为这是一个错误或可以改进的地方,最后根据我评论的改进给出了我的解决方案关于。

class Node {
    /* 
        I assume this is for backup? 
        but this dosn't work becouse every instance of the Node object as that property, 
        even if its value is undefined.
        So it's better to just give defualt values in the constructor
    */
    name = '';
    children = [];
/*
    childNames is an array of what? ['A', 'B'] or [{name: 'A', children: []}, ... ]
    second case → you should make sure that they are instances of Node,
    so you can use the addChild methos
*/

//for default values: (name='', childNames=[])

constructor(name, childNames) {
    this.name = name;
    this.children = childNames;
    // if this children are not nodes you can't use the addChild method 
}

createNodeWithChildren(name, childNames) {
    /* 
        There are a few problems with this method:
        1. the name of the method, createNodeWithChildren,
           you aren't even creating a node, but recreating it. 
        2. you are modifying the node object with the keyword 'this',
           and than returning a new Node object that isn't even saved
           in a variable
        →  or you use the 'this' keywor or return a new object, but like I
           sad you should create a new node with children and not a children 
           on an existing node
    */

    // 'this' points at the eNode Object → {name: 'E', children: []}
    this.name = name; 
    // → {name: 'E', children: []}
    const childArray = [];   // don't know why is here
    this.children = childNames.map(childName => {
        return { name: childName, children: [] };
    }); 
    /*  
        → {name: 'E', children: [{ name: 'Q', children: [] }, { name: 'R', children: [] }]}
        they are non-instance of Node, they aren't real nodes but similar objects, so you 
        can't use the addChild method on them
    */
    console.log(`${this.name}: ${JSON.stringify(this.children)}`);
    return new Node( this.name, this.children);
}

/*
    in the end you are still passing a children variable, so why not have
    only one method to add the node and children?
*/
    addChild({ name, children }) {
        const newNode = new Node(name, children);
        console.log("NODE", JSON.stringify(newNode));
        this.children.push(newNode);
    }
}

    // Graph is just a handle to a bunch of nodes.  Add the three child nodes under node A's children property (B (this.root.children[0]), C(this.root.children[1]), D(this.root.children[2]))
    const graph = new Node('A', []);
    const bNode = new Node('B', []);
    const cNode = new Node('C', []);
    const dNode = new Node('D', []);
    graph.addChild(bNode);
    graph.addChild(cNode);
    graph.addChild(dNode);
    
/*************************************/
// add to bNode this.root.children[0]
let eNode = new Node('E', []);
eNode.createNodeWithChildren('E', ['Q', 'R']);
//createNodeWithChildren return a new Node object, but you aren't saving it
//the value of eNode changed because of the keyword this
//either you return a value or use the this keyword
console.log(`new Enode:`, JSON.stringify(eNode));

let fNode = new Node('F', []);
graph.children[0].addChild(eNode);
graph.children[0].addChild(fNode);
console.log(`Graph: ${JSON.stringify(graph)}`);


/*
    TESTS
*/
 
// 1. show values of empty node object 
let d = new Node();
console.log(d);
// → Node { name: undefined, children: undefined }


// 2. are the children of eNode real nodes? 
console.log(eNode.children[0] instanceof Node);
// → false
console.log(eNode.children.addChild);
// → false

我的解决方案: 因为这个图基本上是一棵树,所以我将它命名为 TreeNode

class TreeNode {
    // TreeNode Object = {name: 'A', children: [{name: 'B', children: []}, {name: 'C', children: []}]}
    //childNames = array of TreeNode Objects
    constructor(name = '', childNames = []) {
        this.name = name;
        //makes sure the child object is an instance of this class
        this.children = childNames.map((childName) => new TreeNode(childName, []));
    }

    addChild(node) {
        //you can add a child to an already existent node or create one
        if(!(node instanceof TreeNode)) {
            const {name, children} = node;
            node = new TreeNode(name, children);
        }
        console.log("NODE", JSON.stringify(node));
        this.children.push(node);
    }

}

//root node
const tree  = new TreeNode('A', ['B', 'C']);

//non-root nodes
const eNode = new TreeNode('E', []);
const fNode = new TreeNode('F', []);

/*************************************/

//adding children
tree.children[0].addChild({name: 'D', children: ['H']}); //non-instance
tree.children[1].addChild(eNode);  //instance of TreeNode


console.log(tree);
console.log(tree.children[0]);
console.log(tree.children[0].children[0]);