January 30, 2020
TestsSource
- var, let, const
- hoisting
- nested scope + hoisting
- lexical vs dynamic scoping
Source Code
The source code we wrote during the event.
scope.spec.js
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);
});
});