Before let existed, developers wrapped loop bodies in an IIFE and passed the loop variable in as a parameter, creating a new scope per iteration. The argument j receives a copy of i’s current value, so each iteration captures 0, 1, and 2 instead of the shared final i. This is the historical pattern that let’s per-iteration binding later made unnecessary.