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 workBut global variables are accessible inside functions.
// global scope
var result = 0;
function adder(x, y) {
result = x + y
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) {
result = x + y;
return result;
}
console.log(result) // Uncaught ReferenceError: result is not defined
adder(3,2)
console.log(result) // 5HOISTING
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 const1. And they changed how
var works.
So now all vars (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 // errorTHIS
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}!`)
}
}
fruit.print() // I am a yellow banana!
const anotherfruit = { name: 'apple', color: 'red' }
anotherfruit.print = fruit.print
anotherfruit.print() // I am a red apple!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}!`)
}
}
fruit.print() // I am a yellow banana!
// this === fruit
const fruit = {
name: 'banana',
color: 'yellow',
print: () => {
console.log(`I am a ${this.color} ${this.name}!`)
}
}
fruit.print() // I am a undefined !
// this === Window