One of iteration Conway's Game of Life
There are a couple of ways of solving this:


Nest the other functions inside of oneIterationConway

function oneIterationConway(matrix) {
  const height = matrix.length;
  const width = matrix[0].length;
  function isInBounds(row, col, matrix) {
    return row &gt;= 0 &amp;&amp; col &gt;= 0 &amp;&amp; row &lt; height &amp;&amp; col &lt; width;
  }


this way the first definition of height is in scope (available) inside isInBounds. Hiding your 'private' functions is a good idea anyway.
Don't use isInBounds at all, take advantage of the fact that items out of bounds are undefined.

function countNeighbors(row, col, matrix) {
  let neighbors = -matrix[row, col];

  for (let i = -1; i &lt;= 1; i++) {
    for (let j = -1; j &lt;= 1; j++) {
      let value = matrix[row+i, col+j];
      if (value) neighbors++;
    }
  }
  return neighbors;
}

If I am in control of the design, something I often do is add a boundary around my matrix. Something like:

const testMatrix = [
  [0, 0, 0, 0, 0, 0, 0],
  [0, 1, 0, 0, 0, 0, 0],
  [0, 0, 1, 1, 0, 0, 0],
  [0, 0, 1, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0],
];



My loops would then be from 1 to size-2

The only other obvious thing I would be would be to factor out part of the main loop:

for (let row = 0; row &lt; height; row++) {
  resultMatrix[row] = [];
  for (let col = 0; col &lt; width; col++) {
    resultMatrix[row][col] = isAlive(row, col);
  }
} 

function isAlive(row, col) {
  const neighborCount = countNeighbors(row, col);
  if (matrix[row][col]) {
    // current cell is alive
    return neighborCount &gt; 1 &amp;&amp; neighborCount &lt; 4 ? 1 : 0;
  } else {
    // current cell is dead
    return neighborCount === 3 ? 1 : 0;
  }
}


btw, There is a nice feature in most environments. You can use: console.table(results)