Memory Management - part1
Introduction
This note is an introduction to the memory management topic in JavaScript language.
This part will explain the basic terms like memory management and differences between primitives and references data structure.
Memory Management
Memory Management - A form of resource management applied to computer memory. Memory management resides in hardware, in the operating system, and in programs and applications. In the application layer, this process applies to control and coordinating the way a software accesses computer memory.
The applications need access to the computer RAM (Random-access memory) to perform operations like storing values and data structures that are used during program execution and loading the bytecode that needs to be executed.
The responsibility of memory management in JavaScript takes the JavaScript engine. There are several JavaScript engines.
- V8 - Used in Node.js and in Google Chrome browser, it is the most popular JavaScript engine
- SpiderMonkey - The engine used in Firefox
- Chakra - used in Microsoft Edge
When the variable is declared by the program, some chunk of memory is allocated. There are two types of memory allocation: stack memory and heap memory.
The role of the JavaScript Engine is to determine if the value is a primitive or reference value. Depending on it, the value will be stored on the stack or in the heap.
Primitives
In JavaScript are 6 primitive data types:
- string (in opposite to other programming languages, JavaScript tracts strings as primitive data type)
- number
- boolean
- symbol
- null
- undefined
Primitive types are simple atomic pieces of data, they do not contain both methods and properties and they are immutable (cannot be altered but can be reassigned) and they are stored on the stack. Access to the primitives is via value, that means, when you working with them you are manipulating the actual value stored in a variable.
1let foo = 'foo'2foo.bar = 'bar'3console.log(foo.bar) // undefined45foo.repeat(2) // primitive boxing6console.log(foo) // foo78foo = foo.repeat(2) 9console.log(foo) // foofoo
There are several options to create primitive values, but the most common and also recommended is using literals. Literals in programming represent a value written exactly as it's meant to be interpreted.
1let x = 52x = 'foo' 34// object literal5const foo = { foo: 'foo', bar: 'bar'}
The other way to create the primitives is by using the constructor function without new keyword. This way is mostly used to casting values.
1let x = '5'2x = Number(x)34const y = Boolean(null)
The last way to create literals is by using constructors and then manually unboxing the primitive value from the object. This way is not recommended and it may lead to unexpected results.
1const foo = new String('foo')2console.log(typeof foo) // object34console.log(foo.valueOf()) // 'foo'5console.log(typeof foo.valueOf()) // 'string'
Boxing variables takes place automatically and there is no need to box variables manually.
1const foo = 'foo'2console.log(foo.length) // 3 -> In this case string primitive was boxed on the fly by the String object.
Objects
In JavaScript, everything that is not a primitive value is an object. Objects are reference values that may be made up of multiple variables. Objects are a more complex data structure than primitives and can be seen as a collection of properties. Nearly all objects in JavaScript are instances of Object. Objects are stored in the heap. Access to the objects is via reference.
Built-in objects:
- String
- Number
- Boolean
- Object
- Function
- Array
- Date
- RegExp
- Error
Examples:
1const foo = () => {}2const bar = { bar: 'bar' }3const baz = [1, "2", null]4const ban = new Set([1, 2, 3])5const qux = new Map([['foo', 'bar']])
Objects in opposite to the primitives are mutable.
1const foo = { bar: 'foo' }2foo.bar = 'bar'3foo.baz = 'baz'45console.log(foo.bar) // 'bar'6console.log(foo.baz) // 'baz78const bar = [1, 2, 3]9bar.reverse()1011bar // [3, 2, 1]
Objects can be created by using the literals form and also by using constructors.
1let obj = {2 foo: 'foo',3 bar: function () {4 return this.foo5 }6}78let obj = new Object({9 foo: 'foo',10 bar: function () {11 return this.foo12 }13})1415let obj = new Object()16obj.foo = 'foo'17obj.bar = function () { return this.foo }
Argument Passing
In JavaScript all arguments passed to the function are passed by value.
1const x = 1023function increment (number) {4 number += 15 return number6}78console.log(x) // 10

In the above example, we are passing to the function primitive value. Function increment takes as an argument the value which is a number 10. In the function scope, it is created a new variable with value 10 then it is incremented and returned. The variable x wasn't changed.
1const x = { name: 'James', age: 40 }23function rename (person) {4 person.name = 'Jack'5 return person6}78console.log(person.name) // Jack

In this example, we are also passing to the function value, but in this case, the value of the variable x is a reference. When the person variable is created in the scope of rename function it indicates the same object as variable x. As a result the object x will be changed.
Copying Values
The same rule as in argument passing applies to the copying values. Primitives values are copied without side effects. When the objects are copied their value is a reference to the object on the heap.
1let x = 102let y = x3let z = y45x += 1067x // 208y // 109z // 10

1const x = { foo: 'foo', bar: 'bar' }2const y = x3const z = y45delete x.foo67x // {bar: "bar"}8y // {bar: "bar"}9z // {bar: "bar"}

Summary
This note covers basics information about memory management in JavaScript. It was explained what is memory management, differences between the primitives, and references values.
In the next note with this series will be covered differences between the stack and heap.