結論
const n = 4;
const arr1 = new Array(n).fill(null).map(_ => []);
理由
fill の中身にオブジェクトを放り込むと、シャローコピーで反映されるため
const n = 4;
const arr1 = new Array(n).fill([]);
arr1[0][0] = 1;
// [[1], [1], [1], [1]]
console.log(arr1);
これは仕様としてキチンと記載されている
// 配列の各スロットから参照される、単一のオブジェクト。
const arr = Array(3).fill({}); // [{}, {}, {}]
arr[0].hi = "hi"; // [{ hi: "hi" }, { hi: "hi" }, { hi: "hi" }]
なお、fill をすっとばすと意図しない動作になる
良い書き方が思いつかなかったのでこれで妥協