JSlang.dev

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

March 23, 2018
TestsSource
Functions

Source Code

The source code we wrote during the event.

import assert from 'assert';

const globalScope = typeof window !== 'undefined' ? window : global;

describe('Function', function() {
  it('is of type `Function`', () => {
    assert.equal(typeof Function, 'function');
  });
  it('returns a function when used as constructor', () => {
    assert.equal(typeof (new Function()), 'function');
  });
  it('returns a function when called', () => {
    assert.equal(typeof (Function()), 'function');
  });
  it('executing a "new" Function', () => {
    const fn = new Function('return 42');
    assert.equal(fn(), 42);
  });
  it('executing a "new" Function (without `new`)', () => {
    const fn = Function('return 42');
    assert.equal(fn(), 42);
  });
  it('eval inside `Function`', () => {
    const fn = Function('return eval("42")');
    assert.equal(fn(), 42);
  });
  it('return "this" from inside `Function`', function() {
    const fn = Function('return this');
    assert.equal(fn(), globalScope);
  });
  it('return "this" from inside `new Function`', () => {
    const fn = new Function('return this');
    assert.equal(fn(), globalScope);
  });
  it('with 2 arguments', () => {
    const fn = new Function('x', 'return x');
    assert.equal(fn(42), 42);
  });
  xit('"normal function" the scope of `this` ???', function() {
    function fn() { return this; };
    assert.equal(fn(), this);
  });
  
});

describe('closure', () => {
  // - function has a ref to smthg that does not exist anymore

  it('function has access to outside scope', () => {
    function fn1() {
      const someVar = 42;
      return function fn2() {
        return someVar;
      }
    }
    assert.equal(fn1()(), 42);
  });
  it('inside^2 function still has outside scope access', () => {
    function fn1() {
      const someVar = 42;
      return function fn2() {
        const anotherVar = someVar;
        return function fn3() {
          return someVar + anotherVar;
        }
      }
    }
    assert.equal(fn1()()(), 84);
  });
  it('inside^3 function still has outside scope access', () => {
    function fn1() {
      const someVar = 42;
      return function fn2() {
        const anotherVar = someVar;
        return function fn3(a) {
          return someVar + anotherVar + a;
        }
      }
    }
    assert.equal(fn1()()('1'), 841);
  });
  it('duplicated names', () => {
    function fn1() {
      const someVar = 42;
      return function fn2(someVar) {
        return someVar;
      }
    }
    assert.deepEqual(fn1()(), undefined);
  });
  
  describe('this "stuff"', function() {
    xit('`this.x` in nested function', function() {
      function fn1() {
        this.x = 42;
        // return function fn2() {
        //   return this.x;
        // }
        return this.x;
      }
      assert.deepEqual(fn1(), window.x);
      
    });
  });
  
  it('immediatly invoked fn', (done) => {
    var bar = '';
    for (var x = 0; x<10; x++) {
      const fn = function(y) {
          // var y = x;
          return function() {bar += y;}
        }(x);
        
      setTimeout(fn, 1);
    }
    setTimeout(() => {assert.equal(bar, '0123456789'); done();}, 2);
  });
  it('using `setTimeout`', (done) => {
    var bar = '';
    for (var x = 0; x<10; x++) {
      const fn = (y) => { bar += y;  };
      setTimeout(fn, 1, x);
    }
    setTimeout(() => {assert.equal(bar, '0123456789'); done();}, 2);
  });
  it('using `bind`', (done) => {
    var bar = '';
    for (var x = 0; x<10; x++) {
      const fn = (y) => { bar += y;  };
      setTimeout(fn.bind(null, x), 1);
    }
    setTimeout(() => {assert.equal(bar, '0123456789'); done();}, 2);
  });
  it('bind (1)', () => {
    const fn = function() { return this.x }
    assert.equal(fn.bind({x: 42})(), 42);
  });
  it('bind params', () => {
    const fn = function(a, b, c, d) { return this.x + a + b + c + d}
    
    const fnToCall = fn.bind({x: 42}, 1, 2);
    assert.equal(fnToCall(5, 10), 60);
  });

  it('binding again does NOT change the scope', () => {
    const fn = function() { return this.x}
    
    const fnToCall = fn.bind({x: 42}).bind({x: 23});
    assert.equal(fnToCall(), 42);
  });

  it('binding again, can bind another param', () => {
    const fn = function(a, b) { return a + b}
    
    const fnToCall = fn.bind(null, 1).bind(null, 2);
    assert.equal(fnToCall(), 3);
  });

});