scope
how scope works in javascript
2024-02-25
CONTENTS
- INTRODUCTION
- FUNCTION AND GLOBAL SCOPE
- HOISTING
- LEXICAL SCOPE WITH LET AND CONST
- THIS
- ARROW FUNCTIONS
INTRODUCTION
Hey, I heard youāre interested in how scope works!
Scope in javascript is wonderfully complicated. Iām going to tell you all that I know about it in case you find it interesting or relevant to your learning.
FUNCTION AND GLOBAL SCOPE
So in the beginning, javascript only had function scope and global scope.
Because of function scope, variables declared inside a function are not accessible outside that function.
// function scope
function adder(x, y) {
var result = x + y
}
console.log(result); // doesn't work
But global variables are accessible inside functions.
// global scope
var result = 0;
function adder(x, y) {
= x + y
result return result;
}
console.log(result); // works!
So when looking for variable definitions, the compiler will first look inside the current function, and will then look inside the enclosing function, if any, and continue until it reaches the global scope. And if itās not there, then itās a reference error.
Insidiously, if you reference a variable that hasnāt been declared in function scope or in global scope, it will be added to the global scope for you!
const adder = function adder(x, y) {
= x + y;
result return result;
}console.log(result) // Uncaught ReferenceError: result is not defined
adder(3,2)
console.log(result) // 5
HOISTING
As an aside, javascript also has name hoisting.
In some languages like C, you can only refer to functions and variables that you have already declared earlier in your code. That is, you must declare variables before you can reference them.
For example, in such a language, this would be an error:
();
adder
int adder(int x, int y) {
return x + y;
}
ā¦because youāre trying to reference adder becuase you have defined it.
But javascript donāt care!
Javascript goes through and finds all those function definitions and stuff and then HOISTS them to the top of the scope. So you can reference functions before you declare them.
LEXICAL SCOPE WITH LET AND CONST
When ECMA of
the Coast published JSNext, the introduced a new kind of scope, which is
lexical or block scope. And they introduced some new keywords to mess
around with this scope: let
and const
1. And they changed how
var
works.
So now all var
s (which is 100% of all variables in
existing code at the time of ESNext because thatās the only keyword we
had for declaring variables) are now global variables. They break out of
their function scope and are global now.
New let
and const
keywords now create block
scope. So now you can have values scoped locally to if
statements and other blocks as well as to functions.
so you can have something like:
const result = 24
if (result == 24) {
const result = 36
console.log(result) // 36
}
let const = 48 // error
THIS
this
is one of javascriptās quirkiest quirks.
functions and objects in javascript have a context that you can
access with the keyword this
.
const fruit = {
name: 'banana',
color: 'yellow',
print() {
console.log(`I am a ${this.color} ${this.name}!`)
}
}
.print() // I am a yellow banana!
fruit
const anotherfruit = { name: 'apple', color: 'red' }
.print = fruit.print
anotherfruit.print() // I am a red apple! anotherfruit
There is a family of function methods, bind
,
apply
, and call
, that take a
this
context as its first argument.
The one time I have continued to run into this
over and
over again is with eventlisteners. Sometimes when you are adding an
event listener to an element, you want to be able to pass some variables
into the callback function to do something with them.
This wonāt work because despite your wishful thinking, the callback function is passed the event object as its first parameter:
const fruit = {
name: 'banana',
color: 'yellow',
}
function handleClick(fruit) {
window.alert(fruit.name) // doesn't work
}
document.querySelector('#clickybutton')
.addEventListener('click', handleClick(fruit));
But using bind
will work!
const fruit = {
name: 'banana',
color: 'yellow',
}
function handleClick(fruit) {
window.alert(fruit.name)
}
document.querySelector('#clickybutton')
.addEventListener('click', handleClick.bind(fruit)); // works!
ARROW FUNCTIONS
Of course there are now arrow functions. These donāt just look cool. They also also close over their parent scope.
const fruit = {
name: 'banana',
color: 'yellow',
print() {
console.log(`I am a ${this.color} ${this.name}!`)
}
}.print() // I am a yellow banana!
fruit// this === fruit
const fruit = {
name: 'banana',
color: 'yellow',
print: () => {
console.log(`I am a ${this.color} ${this.name}!`)
}
}.print() // I am a undefined !
fruit// this === Window