JSlang.dev

JavaScript Deep Dives with Wolfram. Since 2015
Inclusive, Test-Driven, Spec-Focused, Collaborative.

June 19, 2024
TestsSource
#73 closures

Source Code

The source code we wrote during the event.

import {strict as assert} from 'assert';

describe('how far can we nest a closure?', () => {
  it('an inner function has access to the parent function`s scope', () => {
    function outer() {
      let varInOuter = 42;
      return function inner() {
        varInOuter += 1;
        return varInOuter;
      }
    }
    assert.equal(outer()(), 43);
  });
  it('an 2nd level inner function has accces to the outer scope', () => {
    function veryOuter() {
      let varInVeryOuter = 42;
      return function outer() {
        varInVeryOuter += 1;
        return function inner() {
          varInVeryOuter += 1;
          return varInVeryOuter;
        }
      }
    }
    assert.equal(veryOuter()()(), 44)
  });
});

describe('`var` vs. `let` in a closure?', () => {
  it('the inner function has access to the outer function`s scope even using `var`', () => {
    function outer() {
      var varInOuter = 42;
      return function inner() {
        varInOuter += 1;
        return varInOuter;
      }
    }
    assert.equal(outer()(), 43);
  });
});

describe('dynamically created closures', () => {
  it('using a for-loop and passing the loop variable to a closure makes each closure use the loop variable', () => {
    const closures = [];
    for (var i= 0; i<3; i++) {
      closures.push(() => i);
    }
    assert.equal(closures[0](), 3);
    assert.equal(closures[1](), 3);
    assert.equal(closures[2](), 3);
  });
  it('creating a closure using `let` fixes the above "problem"', () => {
    const closures = [];
    for (let i= 0; i<3; i++) {
      closures.push(() => i);
    }
    assert.equal(closures[0](), 0);
    assert.equal(closures[1](), 1);
    assert.equal(closures[2](), 2);
  });
  it('use an IIFE to fix the problem even using `var`', () => {
    const closures = [];
    for (var i= 0; i<3; i++) {
      const newFunction = (function(newI) {
        return function() {
          return newI;
        }
      })(i);
      closures.push(newFunction);
    }
    assert.equal(closures[0](), 0);
    assert.equal(closures[1](), 1);
    assert.equal(closures[2](), 2);
  });
});

describe('block `{ }` has the same rules like closures?', () => {
  it('an inner block has access to the outer block', () => {
    {
      let outerVar = 42;
      {
        assert.equal(outerVar, 42);
      }
    }
  });
  it('a block scoped `let` variable is not visible outside', () => {
    {
      let inside = 42;
    }
    assert.throws(() => {
      inside;
    }, ReferenceError);
  });
  it('a block scoped `var` var is visible outside', () => {
    {
      var inside = 42;
    }
    assert.equal(inside, 42);
  });
});