πŸ“’
SDV503
  • SDV503
  • Command Prompt
    • Windows Command Prompt
    • Mac OS Terminal
  • GIT & GITHub
    • GitHub
    • Git
      • Git Workflow
        • Forking Workflow
  • README
    • How to write a readme.md file?
    • Write a better README.me
    • Generate a README for GitHub Repo Quickly
  • Code-comments
    • Clean Comments
    • Writing Comments in a Good way
  • Pair Coding
    • What is Pair Coding?
    • Pair Programming Experience
  • Programming?
    • What is Programming?
    • What Is A Programming Paradigm?
    • JavaScript Programming Paradigms
  • Number Systems
    • Decimal and Binary numbers
  • JavaSCript
    • Introduction To JavaScript
      • The JavaScript Engine
  • JS Call Stack
    • JavaScript call stack
    • JavaScript & Memory
      • Memory Leaks in JavaScript
    • Execution Context and Execution Stack in Javascript
      • Javascript Execution Context and Hoisting
  • JavaScript Variables
    • Introduction to JS Data Types
    • Primitive and Non-Primitive
    • Operator precedence and associativity
      • JS Operators Part One
      • JS Operators Part Two
      • JS Operators Part Three
    • Clean Code for JS Variables
  • JavaScript Scopes
    • Scope (Chain) Visualized
  • Javascript β€Šβ€”β€Šthis keyword
  • JavaScript Data Types
    • More JavaScript Data Types
  • JavaScript Expressions and Statements
  • if/else, switch and ternary operator
    • If/Else statement
  • Switch Statement
  • Ternary Operator
  • JavaScript Loops
    • Loops in JavaScript
      • Trouble With Loops
  • Objects
    • JavaScript Objects
      • Prototypal Inheritance Visualized
      • JavaScript Number Object
      • JavaScript String Object
  • Functions
    • JavaScript Function Part One
    • JavaScript Function Part Two
    • Immediately Invoked Function Expressions ~ IIFE
    • JS => Arrow Functions
    • JavaScript Callback
    • Hoisting in JavaScript
      • Hoisting Visualized
    • Recursion Functions
    • Curry and Function Composition
  • JavaScript Features
    • JSpread Operator
    • JavaScript Built-in Functions & Objects
  • Data Structures&Algorithms
    • JavaScript’s Data Types
    • Data Structures in JavaScript
      • Introduction to Big O Notation
      • Big O Notation in Javascript
      • Linked Lists
        • Linked Lists β€” 2
      • Hash Tables
      • Stack & Queue
  • TLDR
    • Single quotes (β€˜ ’) and double quotes (β€œ ”) in JavaScript
  • ES6
    • Generators and Iterators
    • Javascript Classes
    • JavaScript closures
    • JavaScript Promises & Async/Await
      • Event Loop Visualized
  • C#
    • What does C#? (C Sharp)
    • C# vs JavaScript
    • What Is The Difference Between C#, .NET, ASP.NET, Microsoft.NET, and Visual Studio?
    • What is the .NET Framework?
    • Methods and Properties of Console Class in C#
    • Datatypes in C#
    • C# Code Comments
    • The if statement
    • The switch statement
    • Loops
    • Comparison operators
    • Addition assignment operators
    • The String Interpolation Operator
    • Arrays
    • Lists
    • Dictionaries
Powered by GitBook
On this page
  • Variable declarations and assignments for JS primitives
  • JavaScript’s memory model: the call stack and the heap
  • Variable declarations and assignments for JS non-primitives
  • Let vs. const
  • Why Is This Useful for Us to Know

Was this helpful?

  1. JS Call Stack

JavaScript & Memory

PreviousJavaScript call stackNextMemory Leaks in JavaScript

Last updated 4 years ago

Was this helpful?

y// declare some variables and initialize them
var a = 5
let b = 'xy'
const c = true

// assign new values
a = 6
b = b + 'z'
c = false // TypeError: Assignment to constant variable

As programmers, declaring , initializing them (or not), and assigning them new values later on is something we do on a daily basis.

But what actually happens when do this? How does JavaScript in particular handle such basic functionality internally? And more importantly, how does it benefit us as programmers to understand the underlying minutiae of JavaScript?

Variable declarations and assignments for JS

Let’s start off with a simple example. Below, we declare a variable called myNumber and initialize it with a value of 23.

let myNumber = 23

When this code is executed, JS will…

  1. Create a unique identifier for your variable (β€œmyNumber”).

  2. Allocate an address in (will be assigned at runtime).

  3. Store a value at the address allocated (23).

If we were to create a new variable called β€œnewVar” and assign it β€œmyNumber”…

let newVar = myNumber

… since myNumber technically equals β€œ0012CCGWH80”, newVar would also equal β€œ0012CCGWH80”, which is the memory address that holds the value 23. Ultimately, this has the intended effect of colloquially saying, β€œnewVar is now equal to 23.”

Since myNumber equals the memory address β€œ0012CCGWH80”, assigning it to newVar assigns β€œ0012CCGWH80” to newVar.

Now, what happens if I do this:

myNumber = myNumber + 1

β€œmyNumber” will surely have the value of 24. But will newVar also have the value of 24 since they point to the same memory address?

The answer is no. Since primitive data types in JS are immutable, when β€œmyNumber + 1” resolves to β€œ24”, JS will allocate a new address in memory, store 24 as its value, and β€œmyNumber” will point to the new address.

Here’s another example:

let myString = 'abc'
myString = myString + 'd'

While a novice JS programmer may say that the letter β€˜d’ is simply appended to the string β€˜abc’ wherever it exists in memory, this is technically false. When β€˜abc’ is concatenated with β€˜d’, since strings are also primitive data types in JS, a new memory address is allocated, β€˜abcd’ is stored there, and β€œmyString” points to this new memory address.

The next step is to understand where this memory allocation is happening for primitives.

JavaScript’s memory model: the call stack and the heap

For the purposes of this blog, the JS memory model can be understood as having two distinct areas: the call stack and the heap.

The call stack is where primitives are stored (in addition to function calls). A rough representation of the call stack after declaring the variables in the previous section is below.

In the illustrations that follow, I’ve abstracted away the memory addresses to show the values of each variable. However, don’t forget that in actuality the variable points to a memory address, which then holds a value. This will be key in understanding the section on let vs. const.

The heap is where non-primitives are stored. The key difference is that the heap can store unordered data that can grow dynamicallyβ€”perfect for arrays and objects.

Variable declarations and assignments for JS non-primitives

Non-primitive JS data types behave differently compared to primitive JS data types.

Let’s start off with a simple example. Below, we declare a variable called β€œmyArray” and initialize it with an empty array.

let myArray = []

When you declare a variable β€œmyArray” and assign it a non-primitive data type like β€œ[]”, this is what happens in memory:

  1. Create a unique identifier for your variable (β€œmyArray”).

  2. Allocate an address in memory (will be assigned at runtime).

  3. Store a value of a memory address allocated on the heap (will be assigned at runtime).

  4. The memory address on the heap stores the value assigned (the empty array []).

From here, we could push, pop, or do whatever we wanted to to our array.

myArray.push("first")
myArray.push("second")
myArray.push("third")
myArray.push("fourth")
myArray.pop()

Let vs. const

In general, we should be using const as much as possible and only using let when we know a variable will change.

Let’s be really clear about what we mean by β€œchange”.

A mistake is to interpret β€œchange” as a change in value. A JS programmer who interprets β€œchange” this way will do something like this:

let sum = 0
sum = 1 + 2 + 3 + 4 + 5

let numbers = []
numbers.push(1)
numbers.push(2)
numbers.push(3)
numbers.push(4)
numbers.push(5)

This programmer correctly declared β€œsum” using let, because they knew that the value would change. However, this programmer incorrectly declared β€œnumbers” using let, because they interpret pushing things onto the array as changing its value.

The correct way to interpret β€œchange” is a change in memory address. Let allows you to change memory addresses. Const does not allow you to change memory addresses.

const importantID = 489
importantID = 100 // TypeError: Assignment to constant variable

Let’s visualize what’s happening here.

When β€œimportantID” is declared, a memory address is allocated, and the value of 489 is stored. Remember to think of the variable β€œimportantID” as equalling the memory address.

When 100 is assigned to β€œimportantID”, since 100 is a primitive, a new memory address is allocated, and the value of 100 is stored there. Then JS tries to assign the new memory address to β€œimportantID”, and this is where the error is thrown. This is the behavior we want, since we don’t want to change the ID of this very important ID…

When you assign 100 to importantID, you’re actually trying to assign the new memory address where 100 is stored. This is not allowed since importantID was declared with const.

As mentioned above, the hypothetical novice JS programmer incorrectly declared their array using let. Instead, they should have declared it with const. This may seem confusing at the outset. It’s not at all intuitive, I admit. A beginner would think that the array is only useful to us if we can change it, and const makes the array unchangeable, so why use it? However, remember: β€œchange” is defined by the memory address. Let’s take a deeper dive on why it’s totally okay and preferred to declare the array using const.

const myArray = []

When myArray is declared, a memory address is allocated on the call stack, and the value is a memory address that is allocated on the heap. The value stored on the heap is the actual empty array. Visualized, it looks like this:

If we were to do this…

myArray.push(1)
myArray.push(2)
myArray.push(3)
myArray.push(4)
myArray.push(5)

… this pushes numbers onto the array that exists in the heap. However, the memory address of β€œmyArray” has not changed. This is why although β€œmyArray” was declared with const, no error is thrown. β€œmyArray” still equals β€œ0458AFCZX91”, which has a value of another memory address β€œ22VVCX011”, which has a value of the array on the heap.

An error would be thrown if we did something like this:

myArray = 3

Since 3 is a primitive, a memory address on the call stack would be allocated, and a value of 3 would be stored, then we would try to assign the new memory address to myArray. But since myArray was declared with const, this is not allowed.

Another example that would throw an error:

myArray = ['a']

Since [β€˜a’] is a new non-primitive array, a new memory address on the call stack would be allocated, and a value of a memory address on the heap would be stored, the value stored at the heap memory address would be [β€˜a’]. Then we would try to assign the call stack memory address to myArray, and this would throw an error.

For objects declared with const, like arrays, since objects are non-primitive, you can add keys, update values, so on and so forth.

const myObj = {}
myObj['newKey'] = 'someValue' // this will not throw an error

Why Is This Useful for Us to Know

While we would colloquially say, β€œmyNumber equals 23”, more technically, myNumber equals the that holds the value 23. This is a crucial distinction to understand.

Now, the .

JavaScript is the #1 programming language in the world (according to GitHub and Annual Developer Survey). Developing a mastery and becoming a β€œJS Ninja” is what we all aspire to be. Any decent JS course or book advocates for const and let over var, but they don’t necessarily say why. It is unintuitive for beginners why certain const variables throw an error upon β€œchanging” its value while others do not. It makes sense to me why these programmers then default to using let everywhere to avoid the hassle.

variables
primitives
memory
memory address
heap
Stack Overflow’s