Search code examples
javascriptfunctionboolean-expression

Javascript - Why are function calls inside boolean expressions evaluated first?


I'll start of with a simplified example of my problem.

function foo(arr) {
  let limit = 1,
      count = 0;

  for (let i=0, len=arr.length; i<len;) {
    if (arr[i] === '0' || arr[i] === '1') {

      let start = i;
      do {
        if (i - start >= limit) {
          count++;
        }
        i++;
      } while (i < len && arr[i] === '0' || arr[i] === '1');
    } else {
      i++;
    }
  }
  return count;
}

function bar(arr) {
  let limit = 1,
      count = 0;

  for (let i=0, len=arr.length; i<len;) {
    if (arr[i] === '0' || arr[i].charCodeAt(0) === 49) {

      let start = i;
      do {
        if (i - start >= limit) {
          count++;
        }
        i++;
      } while (i < len && arr[i] === '0' || arr[i].charCodeAt(0) === 49);
    } else {
      i++;
    }
  }
  return count;
}

let arr = ['0', '2', '3', '0', '1', '0', '4', '2', '0'];
console.log(bar(arr));
console.log(foo(arr));

The above functions return the total count of occurrences of a pattern in an array, starting the count after a certain consecutive limit. They are simplified examples of my actual function, where I want to evaluate an expression like arr[i].charCodeAt(0) === 49 only if i < len.

The foo function works as expected, but if I include a function call inside my while condition like in bar, it is evaluated first, and it throws a TypeError trying to evaluate an undefined index.

I could move that expression inside the do-while-loop before another i<lencheck. I would like to know why functions are prioritized above "left-to-right" evaluation, and if there is something else I can do to prevent it.


Solution

  • it throws a TypeError trying to evaluate an undefined index.

    because arr[9] doesn't exists, just put a console statement to check the same.

    Demo

    function foo(arr) {
      let limit = 1,
          count = 0;
    
      for (let i=0, len=arr.length; i<len;) {
        if (arr[i] === '0' || arr[i] === '1') {
    
          let start = i;
          do {
            if (i - start >= limit) {
              count++;
            }
            i++;
          } while (i < len && arr[i] === '0' || arr[i] === '1');
        } else {
          i++;
        }
      }
      return count;
    }
    
    function bar(arr) {
      let limit = 1,
          count = 0;
    
      for (let i=0, len=arr.length; i<len;) {
        if (arr[i] === '0' || arr[i].charCodeAt(0) === 49) {
    
          let start = i;
          do {
            if (i - start >= limit) {
              count++;
            }
            i++;
            console.log(i);
          } while (i < len && arr[i] === '0' || arr[i].charCodeAt(0) === 49);
        } else {
          i++;
        }
      }
      return count;
    }
    
    let arr = ['0', '2', '3', '0', '1', '0', '4', '2', '0'];
    console.log(bar(arr));
    console.log(foo(arr));

    Explanation

    Your statement is equivalent of writing

    var a = 1;
    var b = 2;
    a < 1 && b < 2 || true; //return true
    a < 1 && b < 2 || false; //return false
    

    because you have used || in the expression without (), it will get evaluated unless first two expressions are true.

    Use () as

      } while (i < len && (arr[i] === '0' || arr[i].charCodeAt(0) === 49));
    

    Demo

    function foo(arr) {
      let limit = 1,
          count = 0;
    
      for (let i=0, len=arr.length; i<len;) {
        if (arr[i] === '0' || arr[i] === '1') {
    
          let start = i;
          do {
            if (i - start >= limit) {
              count++;
            }
            i++;
          } while (i < len && arr[i] === '0' || arr[i] === '1');
        } else {
          i++;
        }
      }
      return count;
    }
    
    function bar(arr) {
      let limit = 1,
          count = 0;
    
      for (let i=0, len=arr.length; i<len;) {
        if (arr[i] === '0' || arr[i].charCodeAt(0) === 49) {
    
          let start = i;
          do {
            if (i - start >= limit) {
              count++;
            }
            i++;
            console.log(i);
          } while (i < len && (arr[i] === '0' || arr[i].charCodeAt(0) === 49));
        } else {
          i++;
        }
      }
      return count;
    }
    
    let arr = ['0', '2', '3', '0', '1', '0', '4', '2', '0'];
    console.log(bar(arr));
    console.log(foo(arr));