I am working with tree-like structure in Javascript, I found for-loop of Javascript works unexpected way. Let me describe it:
<html>
<head><head>
<body>
<script>
class Test {
constructor(name, children) {
this.children = [];
this.test = () => {
let result = false;
for (let i = 0; i < this.children.length; i++) {
console.log(this.children[i].name);
result = result || this.children[i].test();
}
if (this.name.includes('2')) result = true;
return result;
};
this.name = name;
this.children = children;
}
}
const t = new Test('1', [
new Test('11', [
new Test('111', []),
new Test('112', []),
]),
new Test('12', [
new Test('121', []),
new Test('122', []),
]),
]);
const result = t.test();
console.log(result);
</script>
</body>
</html>
The above code is pure Javascript. A Test
class may have children which are also Test
class. It also have a test()
method which returns boolean, the method has for-loop over its children, calls children’s test()
method, then sums the result with ||
operator.
(So it is implementation of a kind of logical Some() operation on child nodes.)
However, the above code does not call the test()
method of the Test instances with names 121
and 122
.
-
If I replace the following part,
result = result || this.children[i].test();
with
const subResult = this.children[i].test(); result = result || subResult;
then it works fine. It iterates over all the children.
-
If I modify the for-loop as following,
for (let i = 0; i <= this.children.length; i++) { // <----- '<' has been replaced with '<=' if (i === this.children.length) { console.log(i); // this prints '2' after calling '0' } console.log(this.children[i].name); result = result || this.children[i].test(); }
I can see the index of for-loop is jumping out.
-
If I make the method to return
false
by removing theif (this.name.includes('2')) result = true;
then the all children’stest()
methods are called. -
I have tested the above codes in Chrome, Firefox and Safari, and got the same result.
Is this behavior correct? And if it is, can someone explain me the theory?
2
Answers
That’s called Short-circuit evaluation – when the first operand of
||
is truthy, the second operand isn’t executed (no need, since the whole expression is truthy already):https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_OR#short-circuit_evaluation
That’s why you don’t have recursive calls on the children when
result
is true.This is due to Short-circuit evaluation. OR returns the first truthy value and doesn’t evaluate the rest.
When the left hand side of the expression (
result
) is true, the right hand side of the or expression (the function call tothis.children[i].test()
) doesn’t get evaluated, so no function gets called.It’s actually making your program more efficent since when at least one of the children’s names contains 2, you don’t need to test the rest and already know the return value will be true.
The reason
tests every child is because you call the function before the OR expression, whether or not result is
true
, and then use OR only on the already evaluated boolean result.