10 Rules of Javascript Variables

Learning the fundamental elements of variables, constants, and scope (block & function), nested scope and what “lexical scope” means. As well, the Javascript hoist is an archaic but often skill necessary nuance to understand.

in Javascript is the absolute most essential skill you must have to understand the lay of the land. If you can master this first introductory lesson, you will be on your way to being a Javascript developer.

Here are the 10 Rules of Javascript Variables. Unfortunately, you kind of need to grok (understand) them almost all at once, because they interdepend on one another, so going through this material may involve skipping around a bit.

There are 3 types of variables (const, let, and var) one more that exists on the window object that often is considered outside of this paradigm (global).

If you specify no keyword in front of a name (that is, a namespace, or a reference to a variable or function), the JS interpreter will assume you are trying to refer to a variable already in scope, that is, defined in a higher scope that where you currently are (see Rule #4). If you specify let, const, or var, the JS interpreter will assume you are trying to define a new variable, in either block-scope or function-scope.

1. Let vars Can be Mutated.

<div id="what-happened"></div>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>

<script type="text/babel">
  const getMessage = () => {
    let foo = "a thing";
    console.log("let is", foo)
    foo = "something else";
    console.log("foo is now ", foo) // something else
    return foo
  }

  document.getElementById("what-happened").innerHTML = `foo, a let var defined inside of getMessage, was
  initially created as the string 'a thing' and then was changed to ${getMessage()}`;
</script>

2. Constants cannot be mutated.

<div id="what-happened"></div>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>

<script type="text/babel">

const getMessage = () => {
const foo = "a thing";
console.log("let is", foo)
foo = "something else"; // DOES NOT WORK
// Uncaught TypeError: "foo" is read-only
// at _readOnlyError (<anonymous>:5:39)
// at getMessage (<anonymous>:10:21)
// at <anonymous>:17:190
console.log("foo is now ", typeof(foo)) // something else
return foo
}

document.getElementById("what-happened").innerHTML = `foo, a const defined inside of getMessage, was
initially created as the string 'a thing' and then we tried to change it to ${getMessage()}`;
</script>

DOES NOT WORK:

3. let and const keywords apply to block-scope, which is a scope within the function scope where the block is.

Block scope is a special kind of scope subset that will exist within wherever the block was defined.

Blocks are found in if, switch, and for statements.

<div id="what-happened"></div>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>

<script type="text/babel">
  const getMessage = (x) => {
    if (x % 2 == 0) {
      let foo = "the variable you passed was";
      foo =+ x;
      console.log("let is", foo);
      const bar = "an even number";
    }

    console.log("foo is now ", typeof(foo))
    // foo and bar are both undefined outside of the if statement
    // that's because they exist in BLOCK-scope only.
    // Remember, the block has access to the in-scope function scope variables
    // any block-scope variables defined in this block

    // Uncaught ReferenceError: foo is not defined
    // at getMessage (<anonymous>:15:3)

    return foo
  }

  document.getElementById("what-happened").innerHTML = `foo, a const defined inside of getMessage, was
  initially created as the string 'a thing' and then we tried to change it to ${getMessage()}`;
</script>

Let and const were added in ECMAScript 2015, also known as ES6.

4. Var applies to function scopes. It was from the early days of Javascript but its core concept is the most important thing to understand about working with JS.

Const + Let both exists in block scope; whereas var exists in function scope. ‘Existing in function scope’ means it behaves the same way a variable and a function behave, which is that that can be nested within eachother, variables are bound to the scope where they are created, and likewise cease to exist when that scope exists.

<div id="what-happened"></div>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>

<script type="text/babel">
  const getMessage = (x) => {
    var isEven;
    if (x % 2 == 0) {
        isEven = true;
    }
    console.log("is_even is now ", typeof(isEven))
    return !!isEven
  }

  document.getElementById("what-happened").innerHTML =
    `when the number is 3, and getMessage returns ${getMessage(3)}` +
    `when the number is 4, and getMessage returns ${getMessage(4)}` +
    `Here, we can see that vars created in function scope are within the universe of the blocks`;
</script>

You’ll notice that in the above code, isEven gets initialized as a var early, but if the if statement doesn’t execute (because it is odd), it will still be undefined. The !!isEven is a construction you may see often in programming that forces whatever data you are working with to be either true or false, even if it was only ‘truthy’ (that means truth-like, or evaluates to true) or falsy (evaluates to false, like it does in this case, undefined). !! just means “take this thing and make it true or false, even if it is a string or 0 or a numeric value.” Remember, the six falsy values of Javascript are false, the number 0, undefined, null, empty string, and Non-A-Number (NaN). If you do !! on any of those things, you will always get false. The !! when performed on anything else will always give you true.

None of this is necessary in Typescript, which enforces strict type-checking, and why the industry has moved on from Javascript to Typescript. However, when working with legacy code, you may still encounter this syntax.

Let’s Review

immutablemutable
block-scopeconstlet
function scopevar

5. Scopes are Nested to Create Universes within Universes

Inner Scope has Access to the Outer Scope.

<div id="what-happened"></div>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>

<script type="text/babel">

function myOuterUniverseFunc() {
// the outer universe
let outerVar = "I'm from the outer universe!";

function myInnerUniverseFunc() {
// the inner universe
outerVar += "<< inner universe was here"
return outerVar
}
return myInnerUniverseFunc;
}
const foo = myOuterUniverseFunc();

document.getElementById("what-happened").innerHTML =
`${foo()}`;

</script>

7. In Javascript, what matters is where it is defined. This is called lexical scoping. The place of definition (the new scope created by a function) and the code itself is called closure.

By now, you should have realized we’re talking about any Javascript namespace, which is a formal word to describe a variable, a constant, or named function (that is, a function assigned to a name) within a given scope.

So the concepts of scope apply to variable names and functions, which are really just thing that is assigned to a variable (hence why Javascript is “functional programming.”)

Image two functions, one nested within the other. Remember, the outer function creates a unverse– we’ll call it my outer universe func. The function within that function creates another universe, called my inner universe function.

The var outer var which is defined in the outer universe, is available to us from inside the nested universe (or nested scope).

What’s going on here is called lexical scoping.

Lexical scoping means that the accessibility of variables is determined by the position of the variables within the nesting function scopes:

the inner universe can access variables from the outer universe.
(or, the inner scope can access variables from the outer scope)

The word “lexus,” from Latin, means dictionary or definition. IN this sense, for the purpose of understanding the scope of the running context you need to look where the function or variable was defined — not where it is executed.

“Lexical” in the context of “lexical scope Javascript” would best be described as “at the place where it is defined.”

As well, the innerFunc() is a closure because it captures the variable outerVar from the lexical scope.

8. Hoisting happens because all namespace as set aside (reserved) at once, at the top of any functional scope.

Hoisting in JavaScript means variables and function declarations are effectively moved to the top of their scope before code execution.

This means that if we do this:

console.log (greeter);
var greeter = "say hello"

it is interpreted as this:

var greeter;
console.log(greeter); // greeter is undefined
greeter = "say hello"

So var variables are hoisted to the top of their scope and initialized with a value of undefined.

9. Modern JS uses ES Import/Export

10. Globals are the last thing you need to learn and the least to remember.

• A Global is a variable that is defined on the window object. It is created when your current scope context is itself also the window context and you create a variable without a keyword, like this

In modern JS, you can’t create globals any longer. The primary reason, in addition to doing mostly inside of React functional components, is that ES import/export doesn’t even give you access to the window object at all.