Data Structures in JavaScript
In this article, I’m going to summarize everything you should know about data structures in JavaScript.
Data Types

The mind graph shows 7 primitive types (left) and 1 reference type object. Among the primitives, Symbol was introduced by ES2015 (ES6) and BigInt by ES2020 (ES11), which means before ES6 there are only 5 primitive types in JavaScript (number, boolean, string, undefined, null)
The difference between the primitive type and the reference type lies in their storage in memory:
- Primitive types are stored in the
Stack.- The value is stored in the stack.
- Passed by value.
- When referenced or copied, an equivalent variable would be created with the exact same value.
- Reference types are stored in the
Heap.- The address that points to the actual value in the heap is stored in the stack (i.e. the reference to the value)
- Passed by reference.
- All references are pointing to the same heap value, which means modification to the value would affect all the references.
Example 1 for References
1 | let a = { |
Since b and a are all pointing to the same object value, modification from b affects the output of a.name
Example 2 for References
1 | let a = { |
Inside the function change, we change the age of the input param to 24, that’s the reason for the output of a. However, we assigned a new object (with its new address) to the input param o, so o then points to a new object. So b is assigned by the return value o, a new object with age 30.
Data Type Detection
Method 1: typeof
1 | typeof 1 // 'number' |
We can see we should only use typeof for number, string, undefined, symbol, bigint, because it’s not accurate for null and reference types (except for function).
Notice that the result for null using typeof is object, the same for array [].
Method 2: instanceof
1 | let Car = function () {} |
Notice that literal string is not the instance of String class.
Let’s try to implement instanceof by ourself for further understanding!
1 | function isInstanceOf(value, type) { |
instanceof can precisely check reference types, but not for primitive types.
So is there a perfect way to check all the data types? Yes, there is.
Method 3: Object.prototype.toString
1 | Object.prototype.toString({}) // '[object Object]' |
toString is the prototype method of Object, by calling it we could get a formated answer for types, i.e. [object Type] (notice that every type is Capitalized). It’s accurate enough for us to detect any data types.
Let’s implement a type detecting method using Object.prototype.toString under the hood:
1 | // return the type in a capitalized string |
Data Type Conversion
Watch out for the == equation operator! It would do implicitly type cast before comparison, and sometimes the result could be unexpected!
1 | '123' == 123 // true |
Therefore, we should always use === for comparison!
And some wired conversions:
1 | Number(null) // 0 |
Type Casting Rules
Type Cast for primitives includes Number(), parseInt(), parseFloat(), toString(), String(), Boolean().
And those wired results of == are all because of the rules of type cast here. So let’s take a closer look.
Rules of Number()
- For boolean,
trueandfalsewould be converted to1and0respectively - For number, return itself
- For null, return
0 - For undefined, return
NaN - For string:
- if the string only contains digits or hex that starts with
0x(sign is allowed), then coverts it to decimal number - if the string is in a valid format of float, then converts it to float number
- if empty string, return
0 - else, return
NaN
- if the string only contains digits or hex that starts with
- For Symbol, throw Error
- For Object:
- If there is
[Symbol.toPrimitive]method, calls it and returns the result - else, call its
valueOf()method, and follows the above rules to return the converted value- if the result is
NaN, the calltoString()method, again follows the above rules to return the converted value
- if the result is
- return
NaNif there is no other choice.
- If there is
1 | Number(true) // 1 |
Rules of Boolean()
Simple Rule:
- if value is
undefined,null,false,'',0 (+0 and -0),NaN, returnfalse - else, return
true
1 | Boolean(0) // false |
Implicit Type Cast
Regarding two values are not the same type, some operators would apply implicit type casting ahead of time, including logical Operators like (&&, ||, !) , operators (+, -, *, /), relation operators (>, <, <=, >=), equality operator (==) and condition if while.
Rules of ==
- if types are the same, no need to cast type
- if one of the value is
nullorundefined, return true if the other one is alsonullorundefined, otherwise false - if one of them is
Symbol, return false - if the two values are
stringandnumber, thenstringvalue would be converted tonumber - if one of them is
boolean, then converts tonumber - if one of them is
objectand the other one isstring,numberorsymbol, then firstly convertsobjectto primitive type viavalueOfortoString.
Rules of +
- if the two are
number, sum them up and return the result - if the two are
string, concat them and return - if one of them is
stringwhile the other isundefined,null, orboolean, then callstoStringof the other value and concats the strings. - if one of them is
numberwhile the other isundefined,null, orboolean, then converts the other to number and sum them up - if the two values are
stringandnumber, then coverts number to string and concats them
1 | 1 + 2 // 3 |
Rules of Object
- If there is
Symbol.toPrimitive, call it and return result - call
valueOf, if the result is primitive, then return - call
toString, if the result is primitive, then return - if none of the above result is primitive, throw error
1 | let obj = { |
Summary
We’ve discussed three aspects of data structures in JavaScript:
- The basics of data types
- The common type detection methods
- Type casting in JavaScript: be careful about the implicit casting!