I have the following code.
class Node {
constructor(value, parent, possibleChildren = []) {
this.value = value;
this.parent = parent;
this.children = []
this.setChildren(possibleChildren);
}
setChildren(possibleChildren) {
if (possibleChildren.length === 0) return [];
while (possibleChildren.length > 0) {
const value = possibleChildren.pop();
// keyword *this* messes up the context. Save them function calls for lazy execution
let childNode = () => new Node(value, this, possibleChildren);
this.children.push(childNode);
}
this.children = this.children.map(child => child())
}
getChildrenValues() {
return this.children.map((child) => child.value);
}
}
In the above the this.children variable is set properly. If I save the this.children array directly, without wrapping it in a function, I see incorrect children being set.
Example:
setChildren(possibleChildren) {
if (possibleChildren.length === 0) return [];
while (possibleChildren.length > 0) {
const value = possibleChildren.pop();
// keyword *this* messes up the context. Save them function calls for lazy execution
let childNode = new Node(value, this, possibleChildren);
this.children.push(childNode);
}
}
I know that the context of this is not consitent without the function wrapper. What I do not understand is why. Any ideas?
Calling getChildrenValues on the first example returns ["A", "B", "C"].
Calling getChildrenValues on the second example returns ["C"]
class Node {
constructor(value, parent, possibleChildren = []) {
this.value = value;
this.parent = parent;
this.children = []
this.setChildren(possibleChildren);
}
setChildren(possibleChildren) {
if (possibleChildren.length === 0) return [];
while (possibleChildren.length > 0) {
const value = possibleChildren.pop();
// keyword *this* messes up the context. Save them function calls for lazy execution
const childNode = new Node(value, this, possibleChildren);
this.children.push(childNode);
}
}
getChildrenValues() {
return this.children.map((child) => child.value);
}
}
let root = new Node(null, null, "ABC".split(""));
console.log(root.getChildrenValues())
I know that the context of
thisis not consistent without the function wrapper. What I do not understand is why?
This has nothing to do with the this keyword. Since you used an arrow function, it does refer to exactly the same object in both your code snippets, there is no difference.
The reason why you get different results from the two snippets is the lazy execution, but not with respect to this, but rather the possibleChildren array. In your first code, the while (possibleChildren.length > 0) runs and empties the possibleChildren array before you do the recursive new Node calls. In the second example, you call new Node during that loop, and you pass on the reference to the same possibleChildren array, which is being emptied by the recursive call and the loop therefore terminates right after the first iteration.
To fix this, just don't recursively pass the possibleChildren:
class Node {
constructor(value, parent, childrenValues = []) {
this.value = value;
this.parent = parent;
this.children = []
this.setChildrenValues(childrenValues);
}
setChildrenValues(childrenValues) {
for (let i=childrenValues.length; i--; ) {
const value = childrenValues[i];
const childNode = new Node(value, this );
// ^ no third argument, no grandchildren
this.children.push(childNode);
}
}
getChildrenValues() {
return this.children.map((child) => child.value);
}
}
let root = new Node(null, null, "ABC".split(""));
console.log(root.getChildrenValues())