In my own words, scope is the space or environment from which a function or a variable is reachable. However, it should rather be: a variable’s or function’s scope is the space or environement it is reachable.
In JS - as oposed to other programming languages - scopes are not defined by blocks, but by functions. Scope - I think at least - is sometimes called a function’s context as well.

  • The scope of an anonymous function is within itself only. It is not reachable from anywhere else, has no button on the outside.
  • The scope of a named function is the block or file it is declared in, regardless of where it is declared, be it the begining or end of that block.
  • The scope of function asigned to a variable is different. Because the scope of variables is different. A variable’s scope - and therefore a function’s scope being the value of a variable - starts at the line of code the variable is declared and ends at the end of the function within which it is defined.

That is the tiny but seemingly important difference to a simple named function. The JS ninja suggests a small code excercise to understand it and really coding this myself and playing around with it did the trick for me!

Code excercise on scope

See the following pairs of screenshots of the excercise steps. The testing results make it crystal clear where scopes of functions and variables begin and end and that they are indeed working differently. What you see is always the same code, only the block of assertions is changed in position, leading to different false and true results of the assertions.

1.) Positioning the assertions completely outside of the outer function code, even above it, the scope of the function is the only one that is reached by the five assertions:


assert(typeof outer === 'function', 'if this message is green, outer is a function and in scope');
assert(typeof inner === 'function', 'if this message is green, inner is a function and in scope');
assert(typeof a === 'number', 'if this message is green, variable a is a number and in scope');
assert(typeof b === 'number', 'if this message is green, variable b is a number and in scope');
assert(typeof c === 'number', 'if this message is green, variable c is a number and in scope');


function outer () {

  var a = 1;

  function inner (){ /* does nothing */ }
  
  var b = 2;

  if (a ==1){
    var c = 3;
  }

}

outer();

scope test 1

2.) Positioning the assertions within the function outer, both functions, but still none of the variables’ scope are reached:

function outer () {

  assert(typeof outer === 'function', 'if this message is green, outer is a function and in scope');
  assert(typeof inner === 'function', 'if this message is green, inner is a function and in scope');
  assert(typeof a === 'number', 'if this message is green, variable a is a number and in scope');
  assert(typeof b === 'number', 'if this message is green, variable b is a number and in scope');
  assert(typeof c === 'number', 'if this message is green, variable c is a number and in scope');

  var a = 1;

  function inner (){ /* does nothing */ }
  
  var b = 2;

  if (a ==1){
    var c = 3;
  }

}

outer();

scope test 2

3.) Moving the assertions further down within the outer function code, now right after variable a ‘s declaration, reaches that variable’s scope, because the scope of variables unlike that of functions, starts only with their declaration. Functions’ scope is anywhere within the function they are declared in, irrespective of where they are declared:


function outer () {

  var a = 1;

  assert(typeof outer === 'function', 'if this message is green, outer is a function and in scope');
  assert(typeof inner === 'function', 'if this message is green, inner is a function and in scope');
  assert(typeof a === 'number', 'if this message is green, variable a is a number and in scope');
  assert(typeof b === 'number', 'if this message is green, variable b is a number and in scope');
  assert(typeof c === 'number', 'if this message is green, variable c is a number and in scope');

  function inner (){ /* does nothing */ }
  
  var b = 2;

  if (a ==1){
    var c = 3;
  }

}

outer();

img

4.) Then the rest becomes pretty logic. Positioning after the inner function is declared doesn’t change anything to the example 3.), inner function’s scope was covered at positions of example 3.) already:


function outer () {

  var a = 1;

  function inner (){ /* does nothing */ }

  assert(typeof outer === 'function', 'if this message is green, outer is a function and in scope');
  assert(typeof inner === 'function', 'if this message is green, inner is a function and in scope');
  assert(typeof a === 'number', 'if this message is green, variable a is a number and in scope');
  assert(typeof b === 'number', 'if this message is green, variable b is a number and in scope');
  assert(typeof c === 'number', 'if this message is green, variable c is a number and in scope');

  var b = 2;

  if (a ==1){
    var c = 3;
  }

}

outer();

img

5.) With this position of the assertions, variable b ‘s scope becomes within scope, variable c still not:


function outer () {

  var a = 1;

  function inner (){ /* does nothing */ }

  var b = 2;

  assert(typeof outer === 'function', 'if this message is green, outer is a function and in scope');
  assert(typeof inner === 'function', 'if this message is green, inner is a function and in scope');
  assert(typeof a === 'number', 'if this message is green, variable a is a number and in scope');
  assert(typeof b === 'number', 'if this message is green, variable b is a number and in scope');
  assert(typeof c === 'number', 'if this message is green, variable c is a number and in scope');

  if (a ==1){
    var c = 3;
  }

}

outer();

img

6.) Still the same as in 5.) variable c is not yet “available”, it is declared after the assertions:


function outer () {

  var a = 1;

  function inner (){ /* does nothing */ }

  var b = 2;

  if (a ==1){

    assert(typeof outer === 'function', 'if this message is green, outer is a function and in scope');
    assert(typeof inner === 'function', 'if this message is green, inner is a function and in scope');
    assert(typeof a === 'number', 'if this message is green, variable a is a number and in scope');
    assert(typeof b === 'number', 'if this message is green, variable b is a number and in scope');
    assert(typeof c === 'number', 'if this message is green, variable c is a number and in scope');

    var c = 3;
  }

}

outer();

img

7.) Only now variable c is caught as well, the assertions are positioned after c’s declaration, all tests turn green:


function outer () {

  var a = 1;

  function inner (){ /* does nothing */ }

  var b = 2;

  if (a ==1){

    var c = 3;

    assert(typeof outer === 'function', 'if this message is green, outer is a function and in scope');
    assert(typeof inner === 'function', 'if this message is green, inner is a function and in scope');
    assert(typeof a === 'number', 'if this message is green, variable a is a number and in scope');
    assert(typeof b === 'number', 'if this message is green, variable b is a number and in scope');
    assert(typeof c === 'number', 'if this message is green, variable c is a number and in scope');

  }

}

outer();

img

8.) This last one I find not intuitive right away, which makes it important. The detail is that the end of the if-block doesn’t turn variable c back to “unavailable”, it’s scope is not terminated by the if-statement, but only with the end of the function it is declared in:


function outer () {

  var a = 1;

  function inner (){ /* does nothing */ }

  var b = 2;

  if (a ==1){

    var c = 3;
  }

  assert(typeof outer === 'function', 'if this message is green, outer is a function and in scope');
  assert(typeof inner === 'function', 'if this message is green, inner is a function and in scope');
  assert(typeof a === 'number', 'if this message is green, variable a is a number and in scope');
  assert(typeof b === 'number', 'if this message is green, variable b is a number and in scope');
  assert(typeof c === 'number', 'if this message is green, variable c is a number and in scope');

}

outer();

img

Thank you ninja. I really needed that on this exact level!