Types in JavaScript

My last few blog posts have focused on changing values from one type to another and the mechanisms that make those coercion operations happen, but I think it's worth taking a step back to fundamentally understand what each of the data types in the language are, what they look like, and when they might get used.

As of ECMAScript Spec Version 12.0, there are eight types within JavaScript. I've seen different numbers at different times and from different sources across the internet, but at the moment of writing, eight types are detailed within the language spec. These are:

  1. undefined
  2. null
  3. Boolean
  4. String
  5. Symbol
  6. Number
  7. BigInt
  8. Object

What follows is by no means comprehensive. There are a lot more details for each of these types, so like in other posts I've included lots of extra resources and further reading. This post is instead a cursory look at the general use cases for each of the eight types in JavaScript at the time of writing this post.

undefined

undefined is a type that occurs when a variable is declared but not assigned a value. For the undefined type, its only value is itself: undefined.

var undefinedThing;
console.log(undefinedThing);
// expected result: undefined

Alternatively, if you are trying to access non-existent properties on an Object, you will encounter undefined:

var person = {
  name: 'Joey',
};

console.log(person.age);
// expected result: undefined

You can also use the void operator to always return undefined. void does not modify the existing value:

var person = {
  name: 'Joey',
};

console.log(void person.name);
// expected result: undefined

console.log(person.name);
// expected result: "Joey"

While the prevailing pattern is that undefined is something the language compiler assigns to values that are declared but not given any other value (or otherwise deliberately voided), you can deliberately assign undefined as a value. However, this is an anti-pattern and should be avoided.

var undefinedThing = undefined;
console.log(undefinedThing);
// expected result: undefined

Speaking of anti-patterns, undefined is a global identifier, or a variable in the global scope. The initial value of this primitive value is itself, undefined. Because of this, you are permitted to use undefined as an identifier. In other words, you can use undefined as a variable name and override the default value of undefined:

function badIdeaNeverDoThis() {
  undefined = 100;
}

badIdeaNeverDoThis();
// global undefined has been reassigned

Rather than assigning anything the value of undefined, you should either use null or the void operator, and if for some reason you ever find yourself using undefined as a variable name or reassigning the value of the global undefined identifier, you should probably re-evaluate what you're trying to do.

undefined gets treated as Falsy within a Boolean evaluation:

var undefinedThing;
if (undefinedThing) {
  console.log("Truthy!");
} else {
  console.log("Falsy!");
}
// expected result: "Falsy!"

You can also use undefined in conjunction with the strict equality/inequality operators to test whether a variable has a value. Because === and !== do not allow for type coercion in their comparison operations, it is safe to proceed knowing that what you're testing truly does or does not have an assigned value:

var undefinedThing;
if (undefinedThing === undefined) {
  console.log("undefinedThing has not been assigned a value");
} else {
  console.log(`undefinedThing has been assigned the value ${undefinedThing}`);
}
// expected result: "undefinedThing has not been assigned a value"

You can also use the typeof operator to check whether a variable has a value:

var undefinedThing;
if (typeof undefinedThing === "undefined") {
  console.log("undefinedThing has not been assigned a value");
}
// expected result: "undefinedThing has not been assigned a value"

There's a big gotcha to be aware of with typeof and undefined though, which is that typeof doesn't throw any error if you were to do the same with a variable that has not been declared:

// otherUndefinedThing has not been declared at all
if (typeof otherUndefinedThing === "undefined") {
  console.log("otherUndefinedThing has not been assigned a value");
}
// expected result: "otherUndefinedThing has not been assigned a value"

It's an important distinction that needs to be made between an "undefined" variable and an "undeclared" variable; the former has been declared with a var or let keyword (reminder that const variables must be assigned a value when they are declared or they will throw a SyntaxError), and the latter has not been declared at all. When you call them outside of any other context, the undefined variable returns undefined and the undeclared variable throws a ReferenceError:

var undefinedThing;
undefinedThing;
// expected result: undefined

otherUndefinedThing;
// expected result: ReferenceError: otherUndefinedThing is not defined

typeof(undefinedThing);
// expected result: "undefined"

typeof(otherUndefinedThing);
// expected result: "undefined"

It's an unfortunate state of things, since the typeof operator would indicate that both the undefined variable and the undeclared variable are of the undefined type and the "not defined" verbiage "ReferenceError: otherUndefinedThing is not defined is so linguistically similar to "undefined". It's grounds for a great deal of confusion.

However, with variables declared with the var keyword, you can additionally test whether that variable exists in its enclosing context using the in operator. For variables at the global scope, that is the window:

var undefinedThing;
if (typeof undefinedThing === "undefined" && "undefinedThing" in window) {
  console.log("undefinedThing has not been assigned a value");
}
// expected result: "undefinedThing has not been assigned a value"

// otherUndefinedThing has not been declared at all
if (typeof otherUndefinedThing === "undefined" && "otherUndefinedThing" in window) {
  // this code never runs
  console.log("otherUndefinedThing has not been assigned a value");
}

Per MDN:

A variable that has not been assigned a value is of type undefined. A method or statement also returns undefined if the variable that is being evaluated does not have an assigned value. A function returns undefined if a value was not returned.

null

null is an assignment value that can be assigned to a variable to represent that that variable holds no value. For the null type, its only value is itself: null.

Unlike undefined, the null value must be deliberately assigned within the code:

var nullThing = null;
console.log(nullThing);
// expected result: null

null gets treated as Falsy within a Boolean evaluation:

var nullThing = null;
if (nullThing) {
  console.log("Truthy!");
} else {
  console.log("Falsy!");
}
// expected result: "Falsy!"

When used with null, typeof returns "object":

var nullThing = null;
typeof(nullThing);
// expected result: "object"

This is commonly seen as a bug within the language, but it's been around for so long that a lot of code in production depends on typeof(null) to return "object". Fixing this bug would create even more bugs and break too much production code to be able to make the case for fixing it.

Per MDN:

The value null represents the intentional absence of any object value. It is one of JavaScript's primitive values and is treated as falsy for boolean operations.

Boolean

The Boolean type represents a logical entity having two possible values: true or false. I like to think of primitive Booleans as being a key to understanding an application's state. For example, determining whether a user is logged in:

var loggedIn = false;

function userLogin() {
  // Do some authentication work
  loggedIn = true;
}

userLogin();

console.log(loggedIn);
// expected result = true

With loggedIn set to true, you can put some conditional logic in place to display certain content based on that state:

if (loggedIn) {
  // display some logged in user content here
} else {
  // display a login form for the not-logged in user here
}

This sort of conditional is what the Boolean type is all about. It's common though to do this kind of check with non-Boolean values, and this is where Implicit Coercion comes in. When you use a non-Boolean value in a context that expects a Boolean, then that non-Boolean value needs to be coerced to a Boolean to determine the outcome of that conditional logic.

Roughly speaking, the following values will always coerce to false, and are thus referred to as "Falsy" values:

  • undefined
  • null
  • +0, -0
  • NaN
  • 0n
  • "" (empty String)

All other values and types coerce to true, and are thus referred to as "Truthy".

See Implicitly Coercing to a Boolean in my Implicit Coercion blog post for more information.

When used with a Boolean, typeof returns "boolean":

var bool = true;
typeof(bool);
// expected result: "boolean"

Per MDN:

The Boolean object represents a truth value: true or false.

String

The String type is any collection of characters contained within single-quotes, '', double-quotes "", or backticks `` that represent text content.

var str1 = "Hello!";
var str2 = 'Howdy!';

The single- and double-quotes essentially function the same - the full String value is wrapped in one or the other in order to construct the String. Backticks `` are a newer, more special case as they allow a developer to embed expressions within the String, and the result of that expression gets interpolated when the String is rendered.

In its simplest use case, this means interpolating a variable's value into the String by noting it with ${[variable or expression name]}:

var greeting = "Hello";
console.log(`${greeting} world`);
// expected result: "Hello world"

The problem this solves is the clunky way in which Strings and variables were historically combined in JavaScript:

var greeting = "Hello";
console.log(greeting + " world");
// expected result: "Hello world"

Much more sophisticated expressions can also be used:

var age = 17;
var ageCheckMessage = `You are ${age < 18 ? 'not' : ''} permitted to vote`;
console.log(ageCheckMessage);
// expected result: "You are not permitted to vote"

In most frontend development work, String values are the text content that gets embedded on the DOM for the user to consume. Sometimes when data comes out of a database or CMS it needs to be manipulated to fit a design, or various content pieces need to be combined. There are a ton of methods available on the String value type to do this kind of manipulation, like .slice(), .substr(), or .substring() to isolate smaller portions of a String or .repeat() to repeat that String value.

Backend development isn't too drastically different from this, but you might also encounter the use of String values as constants to be used across your program. An example of this might be an API key:

Here's some pseudocode for this pattern:

var API_KEY = 'ABCDE12345';

// later on in your program
function async fetchDataFromSomeApi(url, key) {
  var options = {
    url,
    key,
    json: true
  };
  var data = await fetch(options);
};

var response = fetchDataFromSomeApi('https://apiendpoint.dev/givemedata', API_KEY);

You may also have to coerce other types to String representations in order to be able to display them properly on the frontend. Read more about explicit coercion to a String here and implicit coercion to a String here.

An empty String, "", gets treated as Falsy within a Boolean evaluation, but all other String values get treated as Truthy:

var emptyString = "";
var notEmptyString = "howdy";

if (emptyString) {
  console.log("Truthy!");
} else {
  console.log("Falsy!");
}
// expected result: "Falsy!"

if (notEmptyString) {
  console.log("Truthy!");
} else {
  console.log("Falsy!");
}
// expected result: "Truthy!"

When used with a String, typeof returns "string":

var str = "howdy";
typeof(str);
// expected result: "string"

Per MDN:

The String object is used to represent and manipulate a sequence of characters.

Symbol

The Symbol type is a value that represents a unique identifier. A Symbol is created using the Symbol() global function, which accepts a description as its argument:

var sym = Symbol("description");
console.log(sym);
// expected result: "Symbol(description)"

The Symbol type is mainly used to provide unique property keys for an Object. These Object properties cannot be accessed or overwritten by other parts of your program. This is very useful when working with a database or importing dependencies then adding your own functionality; by adding a property that is reliably unique, you can ensure that there will not be any code collisions.

Say you're writing a program that registers vehicles in a database, but to help you out you are importing a Vehicle data model from some library:

// in class-vehicle.js
export class Vehicle {
  constructor(make, model, year) {
    this.make = make;
    this.model = model;
    this.year = year;
    this.id = Math.floor(100000000 + Math.random() * 900000000);
  }
  detailCar() {
    console.log(`
      make: ${this.make},
      model: ${this.model},
      year: ${this.year},
      id: ${this.id}
    `);
  }
}

Now in your own program, you import that Vehicle class:

import { Vehicle } from './class-vehicle.js';

var car1 = new Vehicle('Toyota', 'Corolla', 2000);

car1.detailCar();
/*
 * expected result:
 * make: Toyota,
 * model: Corolla,
 * year: 2000,
 * id: 986667798 // or some other random number
 */

However, say you want to add an id property to car1. The issue you'll run into by just doing car1.id = [yourdesiredidnumber] is that you'll be overwriting the id that comes from the Vehicle constructor, which may be necessary for that dependency library to work.

import { Vehicle } from './class-vehicle.js';

var car1 = new Vehicle('Toyota', 'Corolla', 2000);

car1.detailCar();
/*
 * expected result:
 * make: Toyota,
 * model: Corolla,
 * year: 2000,
 * id: 986667798 // or some other random number
 */

car1.id = "MyDesiredID123";

car1.detailCar();
/*
 * expected result:
 * make: Toyota,
 * model: Corolla,
 * year: 2000,
 * id: MyDesiredID123 // Uh oh
 */

A Symbol allows you to add a property named [id] that is truly unique, so it will not collide with the id property you get from the Vehicle constructor:

import { Vehicle } from './class-vehicle.js';

var car1 = new Vehicle('Toyota', 'Corolla', 2000);

car1.detailCar();
/*
 * expected result:
 * make: Toyota,
 * model: Corolla,
 * year: 2000,
 * id: 986667798 // or some other random number
 */

var id = Symbol("vehicleId");
car1[id] = "MyDesiredID123";

console.log(car1[id]);
// expected result: "MyDesiredID123"

console.log(car1.id);
// expected result: 986667798

This is because a Symbol by design is unique. Even two Symbols that share a description will not actually share an identity:

Symbol("howdy") === Symbol("howdy");
// expected result: false

Another neat thing about the Symbol type is that an Object property whose key is a Symbol is not discoverable by a for...in Loop or by Object.keys() or Object.values():

var e = Symbol("someSymbol");
var obj = {
  a: 100,
  b: "Joey Reyes rules",
  c: true,
  d: [1, 2, 3],
  [e]: "JavaScript Drools",
}

for (let key in obj) {
  console.log(key);
}
// expected result: "a", "b", "c", "d" - no "e"

Object.keys(obj);
// expected result: ["a", "b", "c", "d"] - no "e"

Object.values(obj);
// expected result: [100, "Joey Reyes rules", true, [1, 2, 3]] - no "JavaScript Drools"

You can still access the Symbol directly:

var e = Symbol("someSymbol");
var obj = {
  a: 100,
  b: "Joey Reyes rules",
  c: true,
  d: [1, 2, 3],
  [e]: "JavaScript Drools",
}

console.log(obj[e]);
// expected result: "JavaScript Drools"

It's also worth noting that this isn't really a reliable way of stashing away data or functionality you want hidden. Even though the traditional ways of accessing an Object's keys and values won't expose the Symbol property, Object.getOwnPropertySymbols() will:

var e = Symbol("someSymbol");
var obj = {
  a: 100,
  b: "Joey Reyes rules",
  c: true,
  d: [1, 2, 3],
  [e]: "JavaScript Drools",
}

var sneakySymbols = Object.getOwnPropertySymbols(obj);
console.log(sneakySymbols);
// expected result: [Symbol(someSymbol)]

Here we'll get an array that exposes available Symbol descriptions, and from there we can do:

var e = Symbol("someSymbol");
var obj = {
  a: 100,
  b: "Joey Reyes rules",
  c: true,
  d: [1, 2, 3],
  [e]: "JavaScript Drools",
}

var sneakySymbols = Object.getOwnPropertySymbols(obj);
var exposedSymbol = obj[sneakySymbols[0]];
console.log(exposedSymbol);
// expected result: "JavaScript Drools"

So it's a fair amount of work to get to, but you can still retrieve information in an Object that's "hidden" as a Symbol.

All Symbols gets treated as Truthy within a Boolean evaluation:

var sym = Symbol("description");

if (sym) {
  console.log("Truthy!");
} else {
  console.log("Falsy!");
}
// expected result: "Truthy!"

When used with a Symbol, typeof returns "symbol":

var sym = Symbol("description");
typeof(sym);
// expected result: "symbol"

Per MDN:

Symbol is a built-in object whose constructor returns a symbol primitive — also called a Symbol value or just a Symbol — that's guaranteed to be unique. Symbols are often used to add unique property keys to an object that won't collide with keys any other code might add to the object, and which are hidden from any mechanisms other code will typically use to access the object. That enables a form of weak encapsulation, or a weak form of information hiding.

Numeric Types

Numbers in JavaScript can be represented either by the Number type or the BigInt type.

Number

The Number type is used to represent and manipulate numeric values. Just about all numeric work I've ever done in JavaScript has been with Number, not with BigInt.

Numbers are typically created as such:

var num1 = 420;
var num2 = 420.0;

Numbers can get used with mathematical operators such as +, -, /, *, % and so on, as well as the properties and methods found on the Math Object to perform mathematical operations:

// some addition
500 + 166;
// expected result: 666

// the `max` method from `the` Math object
Math.max(666, 420);
// expected result: 666

We can explicitly coerce non-Number values to Number representations using the global Number() function or the parseInt() and parseFloat() functions:

var str = "420";

Number(str);
// expected result: 420

parseInt(str);
// expected result: 420

parseFloat(str);
// expected result: 420

We can also implicitly coerce non-Number values to Number representations using mathematical operators; note that the + operator also does String coercion so its usage for coercing to a Number has some nuance.

var res1 = 420 - "0";
console.log(res1);
// expected result: 420;

var arr = ["420"];

var num = 100;

var res2 = arr * num;
console.log(res2);
// expected result: 42000

See Implicitly Coercing to a Number in my Implicit Coercion blog post for more information.

0 and -0 get treated as Falsy within a Boolean evaluation; all other Number values get treated as Truthy:

var posZero = 0;
var negZero = -0;
var posNum = 420;
var negNum = -666;

if (posZero) {
  console.log("Truthy!");
} else {
  console.log("Falsy!");
}
// expected result: "Falsy!"

if (negZero) {
  console.log("Truthy!");
} else {
  console.log("Falsy!");
}
// expected result: "Falsy!"

if (posNum) {
  console.log("Truthy!");
} else {
  console.log("Falsy!");
}
// expected result: "Truthy!"

if (negNum) {
  console.log("Truthy!");
} else {
  console.log("Falsy!");
}
// expected result: "Truthy!"

When used with a Number, typeof returns "number":

var num = 420;
typeof(num);
// expected result: "number"

Per MDN:

Number is a primitive wrapper object used to represent and manipulate numbers like 37 or -9.25.

BigInt

The largest numeric value that JavaScript can reliably represent with the Number primitive is 2^53 - 1, which is also represented by the constant Number.MAX_SAFE_INTEGER. In order to represent larger integers, JavaScript introduced the BigInt type.

var bigNum = BigInt(654333333333333333333333333333333333333);
console.log(bigNum);
// expected result: 654333333333333353170768266086452297728n

BigInt values are stored as arbitrary-precision integers, whereas Number values get stored as double-precision 64-bit numbers. Since they are integers, BigInt values don't suffer from floating-point precision problems that Number values do, but the usage of BigInt values is limited by the available memory of the environment they're used in.

MDN lists the differences between Number and BigInt, as well as some usage recommendations:

  • BigInt cannot be used with methods in the built-in Math object
  • BigInt values cannot be mixed with Number values in Math operations
  • Do not coerce between BigInt and Number values, as this can lead to loss of precision
  • Only use a BigInt value when values greater than 2^53 are reasonably expected
  • Do not use BigInts in cryptographic operations; BigInt operations are not constant-time and are open to timing attacks
  • When used on a BigInt, JSON.stringify() will throw a TypeError; in order to serialize a BigInt in JSON, you can:
    • Use .toString()
    • Use JSON.stringify()'s built-in replacer parameter to define how BigInt gets serialized

0n gets treated as Falsy within a Boolean evaluation; all other BigInt values get treated as Truthy:

var zeroBigInt = 0n;
var posBigInt = 1000n;

if (zeroBigInt) {
  console.log("Truthy!");
} else {
  console.log("Falsy!");
}
// expected result: "Falsy!"

if (posBigInt) {
  console.log("Truthy!");
} else {
  console.log("Falsy!");
}
// expected result: "Truthy!"

When used with a BigInt, typeof returns "bigint":

var bigNum = BigInt(654333333333333333333333333333333333333);
typeof(bigNum);
// expected result: "bigint"

Per MDN:

BigInt is a primitive wrapper object used to represent and manipulate primitive bigint values — which are too large to be represented by the number primitive.

Object

The last data type (as of now) is Object. Objects are fundamental to JavaScript, and there is not enough space here to cover all of the nuances about this data type. I highly recommend Kyle Simpson's book, this & Object Prototypes or the second edition update, Objects & Classes for a much richer dive into the Object type and the role that it plays in the language. What I present here is a very, very cursory example of common usage.

The Object type is used to store various keyed collections and represents data that is generally much more complex than primitives, like a Boolean or a String. If you have followed along on this blog for some time, you've seen countless examples of Objects throughout my blog posts. An Object constructed through literal syntax looks something like:

var obj = {
  a: 100,
  b: "Joey Reyes rules",
  c: true,
  d: [1, 2, 3],
  e: function sayHi() {
    console.log("Hi");
  },
  f: {
    a: "JavaScript Drools",
  }
}

Within a set of curly braces {}, there are keys or properties. Here these would be a, b, c, d, and e. The properties are typically followed by a colon :, and on the other side of that colon is the value that is assigned to that key or property. In this example these would be 100, "Joey Reyes rules", true, the sayHi() function, and the { a: "JavaScript Drools" } Object.

The values within an Object can be accessed through dot notation:

var obj = {
  a: 100,
  b: "Joey Reyes rules",
  c: true,
  d: [1, 2, 3],
  e: function sayHi() {
    console.log("Hi");
  },
  f: {
    a: "JavaScript Drools",
  }
}

obj.a;
// expected result: 100

obj.b;
// expected result: "Joey Reyes rules"

obj.c;
// expected result: true

obj.d;
// expected result: [1, 2, 3]

obj.e;
// expected result: function sayHi() { console.log("Hi"); }

obj.f;
// expected result: { a: "JavaScript Drools" }

It's probably clear then by now that typically an Object will represent some kind of collection of common data and functionality, and the values within an Object can be any other data type discussed earlier in this post, as well as other Objects and Arrays. You can see an Array in obj.d and an Object in obj.f. You can get to specific data items within those values with further dot notation or (for Arrays) bracket notation:

var obj = {
  a: 100,
  b: "Joey Reyes rules",
  c: true,
  d: [1, 2, 3],
  e: function sayHi() {
    console.log("Hi");
  },
  f: {
    a: "JavaScript Drools",
  }
}

obj.d[0];
// expected result: 1

obj.f.a;
// expected result: "JavaScript Drools"

All Objects get treated as Truthy within a Boolean evaluation:

var obj = {};

if (obj) {
  console.log("Truthy!");
} else {
  console.log("Falsy!");
}
// expected result: "Truthy!"

The exception to this is a peculiar case called document.all, which is an old, nonstandard, deprecated part of legacy web browsers. The spec says that this very special Object will only return true when compared with null or undefined, making it essentially a Falsy Object. Coercing document.all to a Boolean was a method used to determine whether the code was running in an old, nonstandard version of Internet Explorer.

var obj = document.all;

if (obj) {
  console.log("Truthy!");
} else {
  console.log("Falsy!");
}
// expected result: "Falsy!"

When used with an Object, typeof returns "object":

var obj = {
  a: 100,
  b: "Joey Reyes rules",
  c: true,
  d: [1, 2, 3],
  e: function sayHi() {
    console.log("Hi");
  },
  f: {
    a: "JavaScript Drools",
  }
}

typeof(obj);
// expected result: "object"

Per MDN:

The Object type represents one of JavaScript's data types. It is used to store various keyed collections and more complex entities.

Array

Arrays themselves are a special type of Object. You may have noticed that a typical Object uses String or Symbol values for its key/property names, and that these property names can be just about anything you want and be put into any order that you want. An Array on the other hand uses Numbers as its keys. This special numbered key is called an index, and the order of data corresponds to that data's index. As such, Arrays present a more formal organization than other typical Objects.

All Arrays get treated as Truthy within a Boolean evaluation:

var arr = [];

if (arr) {
  console.log("Truthy!");
} else {
  console.log("Falsy!");
}
// expected result: "Truthy!"

When used with an Array, typeof returns "object":

var obj = {
  a: 100,
  b: "Joey Reyes rules",
  c: true,
  d: [1, 2, 3],
  e: function sayHi() {
    console.log("Hi");
  },
  f: {
    a: "JavaScript Drools",
  }
}

typeof(obj.d);
// expected result: "object"

Per MDN:

The Array object, as with arrays in other programming languages, enables storing a collection of multiple items under a single variable name, and has members for performing common array operations.

Function

functions can also be put into Objects, and typically are done so to help encapsulate functionality common to the data that that Object represents. You can see a function in obj.e. You may note that calling obj.e like the other obj properties simply lists out the contents of that function. But we can also call the function like so:

var obj = {
  a: 100,
  b: "Joey Reyes rules",
  c: true,
  d: [1, 2, 3],
  e: function sayHi() {
    console.log("Hi");
  },
  f: {
    a: "JavaScript Drools",
  }
}

obj.e();
// expected result: "Hi"

Functions themselves are also a special type of Object, called a "callable object". They have special behavior that allows them to be called, meaning the statements within the function body are told to run. But beyond that they can have properties and methods just like any other regular Objects.

All Functions get treated as Truthy within a Boolean evaluation:

function someFunc() {
  console.log("hi");
}

if (someFunc) {
  console.log("Truthy!");
} else {
  console.log("Falsy!");
}
// expected result: "Truthy!"

When used with a Function, typeof returns "function":

var obj = {
  a: 100,
  b: "Joey Reyes rules",
  c: true,
  d: [1, 2, 3],
  e: function sayHi() {
    console.log("Hi");
  },
  f: {
    a: "JavaScript Drools",
  }
}

typeof(obj.e);
// expected result: "function"

Per MDN:

Generally speaking, a function is a "subprogram" that can be called by code external (or internal in the case of recursion) to the function. Like the program itself, a function is composed of a sequence of statements called the function body. Values can be passed to a function, and the function will return a value.

In JavaScript, functions are first-class objects, because they can have properties and methods just like any other object. What distinguishes them from other objects is that functions can be called.

Summary

  • undefined is a type that occurs when a variable is declared but not assigned a value. For the undefined type, its only value is itself: undefined
  • null is a value that can be assigned to a variable to represent that that variable holds no value. For the null type, its only value is itself: null
    • The broad distinction between undefined and null is one of intention: null denotes that the developer intends for there to be no other value, and undefined denotes that a value has not been assigned yet
  • The Boolean type represents a logical entity having two possible values: true or false
  • The String type is any collection of characters contained within single-quotes, '', double-quotes "", or backticks `` that represent text content
  • The Symbol type is a value that represents a unique identifier. A Symbol is mainly used to provide unique property keys for an Object; it's a way to avoid naming collisions and to create "hidden" properties on an Object
  • Numbers in JavaScript can be represented either by the Number type or the BigInt type.
    • The Number type is used to represent and manipulate numeric values. Numbers can get used with mathematical operators and methods from the Math Object to perform mathematical operations
      • Number values get stored as double-precision 64-bit numbers; because of this the Number type can suffer from floating-point precision problems
      • The largest numeric value that JavaScript can reliably represent with the Number primitive is 2^53 - 1
      • The smallest numeric value that JavaScript can reliably represent with the Number primitive is -(2^53 - 1)
    • The BigInt type is used to represent numeric values that are larger than what can be represented by the Number type
      • BigInt values are stored as arbitrary-precision integers, so they don't suffer from floating-point precision problems, but their usage is limited by available memory
      • BigInt cannot be used with methods in the built-in Math object, nor can it be mixed with Number values in mathematical operations
  • The Object type is used to store various keyed collections of data; this data can be comprised of the other types found in the JavaScript language, and generally the Object represents data and functionality that are conceptually related
    • There are a few special types of Objects that are given some special functionality or organization, namely Arrays and Functions:
      • An Array is an Object that uses sequential numeric indexing instead of named properties to achieve a more formal organization pattern to the data that it contains
      • A Function is an Object that contains "callable" behavior

Sources / Further Reading