JSlang.dev

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

January 30, 2020
TestsSource
Scope

Source Code

The source code we wrote during the event.

import assert from 'assert';

describe('var, let, const', () => {
    it('assigning a variable via `var` to `true` is accessible in the same scope', () => {
        var x = true;
        assert.equal(x, true);
    });
    it('create a `var` inside a block is accessible outside of the block', () => {
        if (true) {
            var x = true;
        }
        assert.strictEqual(x, true);
    });
    it('create a `let` inside a block is NOT accessible outside of the block', () => {
        assert.throws(() => {
            if (true) {
                let x = true;
            }
            x;            
        }, ReferenceError);
    });
    it('create a `const` inside a block is NOT accessible outside of the block', () => {
        assert.throws(() => {
            if (true) {
                const x = true;
            }
            x;            
        }, ReferenceError);
    });
    it('create a `const` inside a block using only `{}` is NOT accessible outside of the block', () => {
        assert.throws(() => {
            {
                const x = true;
            }
            x;
        }, ReferenceError);
    });
    it('can override a const-variable in an outer scope', () => {
        {
            const x = true;
        }
        const x = 42;
        assert.strictEqual(x, 42);
    });
    it('using `const` re-declaring a variable throws', () => {
        assert.throws(() => eval(`
            const x = 1;
            const x = 2;
        `), SyntaxError);        
    });
    it('using `var` we can re-declare a variable', () => {
        var x = 1;
        var x = 2;
        assert.strictEqual(x, 2);     
    });
    it('using `let` re-declaring a variable throws', () => {
        assert.throws(() => eval(`
            let x = 1;
            let x = 2;
        `), SyntaxError);   
    });
    it('using `let` and than `var` throws', () => {
        assert.throws(() => eval(`
            let x = 1;
            var x = 2;
        `), SyntaxError);
    });
    it('using `var` and than `let` does throw', () => {
        assert.throws(() => eval(`
            var x = 1;
            let x = 2;
        `), SyntaxError);
    });
    it('can redeclare const in a new block, and does not override the outer block', () => {
        const x = 1;
        {
            const x = 5;
            assert.strictEqual(x, 5);
        }
        assert.strictEqual(x, 1);
    });
    it('can redeclare a var in a new block, and does override the outer block scope', () => {
        var x = 1;
        {
            var x = 5;
            assert.strictEqual(x, 5);
        }
        assert.strictEqual(x, 5);
    });
});

describe('hoisting', () => {
    it('a `var` variable is accessible before declaration and is undefined', () => {
        assert.strictEqual(x, undefined);
        assert.strictEqual(typeof x, 'undefined');
        var x = 1;
    });
    it('an undeclared variable is NOT accessible before declaration and is undefined', () => {
        assert.throws(() => x, ReferenceError);
    });
    xit('an non-strict access of a not declared variable, does not throw', () => {
        // TODO figure out how to use non-strict mode, eval doesnt cut it
        assert.doesNotThrow(() => eval('x'));
    });
    it('a `const` variable is NOT accessible before declaration', () => {
        assert.throws(() => {
            x;
            const x = 1;
        }, ReferenceError);
    });
    it('a function created using a function literal, is available before declaration', () => {
        assert.strictEqual(typeof fn, 'function');
        assert.strictEqual(fn(), 42);
        function fn() { return 42; }
    });
    it('a function created using a function expression, is NOT available before declaration', () => {
        assert.strictEqual(typeof fn, 'undefined');
        assert.throws(() => fn(), TypeError);
        var fn = function() { return 42; }
    });
    it('assigning a named function expression to a variable can NOT be used by the name of the fn', () => {
        assert.strictEqual(typeof a, 'undefined');
        assert.strictEqual(typeof b, 'undefined');
        assert.throws(() => b, ReferenceError);
        assert.throws(() => a(), TypeError);
        assert.throws(() => b(), ReferenceError);
        var a = function b() { return 42; }
    });
});

describe('nested scope + hoisting', () => {
    it('a `var` is accessible in an inner function', () => {
        var x = 1;
        function fn() {
            assert.strictEqual(x, 1);
        }
        fn();
    });
    it('two functions can change the same variable defined a level above', () => {
        var x = 1;
        function fn1() { x = 2 }
        function fn2() { fn1(); assert.strictEqual(x, 2) }
        fn2();
    });
    it('this should work as well :) (DONT DO THAT EVVVA)', () => {
        fn2();
        var x = 1;
        function fn1() { x = 2 }
        function fn2() { fn1(); assert.strictEqual(x, 2) }
    });
    it('simulate `let` behavior using `var`', () => {
        var  x = 1;
        (function() { var x = 2; })();
        assert.strictEqual(x, 1);
    });
    it('??? (we are getting lazy using `var`)', () => {
        fn();
        function fn() {
            assert.strictEqual(x, undefined);
        }
        var x = 1;
    });
    it('??? (we are getting lazy in using `const` #2)', () => {
        fn();
        function fn() {
            assert.throws(() => x, ReferenceError);
        }
        const x = 1;
    });
    it('??? (we are getting lazy in using `const` #3)', () => {
        fn();
        const x = 1;
        function fn() {
            assert.throws(() => x, ReferenceError);
        }
    });
    it('a for-loop using let creates a scope?', () => {
        for (let x of [1,2,3]) {
            // do nothing
        }
        assert.throws(() => x, ReferenceError);
    });
    it('a for-loop using var creates a scope?', () => {
        for (var x of [1,2,3]) {
            // do nothing
        }
        assert.strictEqual(x, 3);
    });
});

describe('lexical vs dynamic scoping', () => {
    it('JS is not like python', () => {
        var x = 1;
        function f() { assert.strictEqual(x, 1); }
        function g() {
            var x = 2;
            f();
        }
        g();
    });
    it('a scope of a function body stays alive even a function returned', () => {
        function f() { 
            var x = 1;
            function g() {
                x = x + 1;
                return x; 
            }
            return g;
        }
        var h = f();
        assert.strictEqual(h(), 2);
        assert.strictEqual(h(), 3);
        var i = f();
        assert.strictEqual(i(), 2);
    });
});