JavaScript course · Part 6: Structure and data · lab 7 of 11
Scope, closures and keeping code tidy
Where a variable can be seen, why a function remembers where it was born, writing functions that are easy to trust, and how files share code with modules.
By the end you can
- Say where any variable can and cannot be seen
- Use a closure deliberately instead of being surprised by one
- Write functions whose behaviour depends only on their inputs
- Explain how modules replace globals, and what import and export do
Easier if you did first
step 1 of 4
Where a variable can be seen
Scope is the answer to one question: from here, which names can I use?
let and const are block scoped. A block is anything between curly brackets: a function body, the inside of an if, the body of a loop. A variable declared in a block exists only in that block.
That is why a const declared inside an if cannot be used after it. The block ended, and so did the name.
Functions can see outwards. A function can use names from the scope that contains it, and from the one containing that, all the way up. It cannot see inwards into other functions.
A name declared at the top level of a file, outside everything, is a global. Globals are visible everywhere, which sounds convenient and is the problem: anything can change them, so when one holds the wrong value, the suspect list is the whole program.
The rule is to declare things in the smallest scope that works, and to pass values into functions rather than reaching out for them.
There is also var, from older JavaScript. It ignores blocks and is scoped to the whole function, which produces surprises. You will meet it in old code. Do not write new ones.
The code on the right has three scope faults. Fix them.
const rate = 45; visible in this file
function quote(guests) {
const total = guests * rate; can see rate, outwards
return total;
}
if (true) {
const inside = 1;
}
console.log(inside); ReferenceError: the block endedWhat these words mean
- scope
- The region where a name can be used.
- block
- Anything between curly brackets.
- block scoped
- let and const exist only inside their block.
- global
- Declared at the top level, visible everywhere. Keep these few.
- shadowing
- An inner name hiding an outer one with the same name.
- var
- The old declaration. Function scoped, full of surprises. Do not use it.
Easy to get wrong
Declaring a variable inside an if and using it after
Declare it before the if
The block ended, so the name no longer exists. You get a ReferenceError, which at least tells you clearly, unlike var, which gives you undefined instead.
A function reaching out to a global it was not given
Pass the value in as an argument
A function that depends on hidden state behaves differently depending on what ran before it, which makes it impossible to test and painful to debug.
What we check
- The fee comes back instead of a ReferenceError
- The discount is passed in and used
- The function takes what it needs as arguments
- It no longer reaches for the global
- No var
Nothing printed yet. Run your code.