DEV Community

Cover image for Ultimate guide to master javascript types: part 1
Methkal
Methkal

Posted on

Ultimate guide to master javascript types: part 1

This article was originally posted on shoufi.tech. If you like the article, Make sure to check the website out and subscribe for more content like this.

Disclaimer: This is not a perfect article however we aim for that through continuous updates and by the valuable feedback that we will receive from our readers.

In the following sections, we will introduce some important basics to prepare you for the long journey to master js types. Please, if you don't understand something then it's ok and we suggest that you come back to this article again once you finish the series. That will help you to connect the missing points and ensure your understanding

Introduction:

In computer science, a primitive data type is either of the following:

  • a basic type is a data type provided by a programming language as a basic building block. Most languages allow more complicated composite types to be recursively constructed starting from basic types.
  • a built-in type is a data type for which the programming language provides built-in support.

Most of the time, a primitive value is represented directly at the lowest level of the language implementation.
In most programming languages, all basic data types are built-in. In addition, many languages also provide a set of composite data types.

Normally, all primitives are immutable, i.e., they cannot be altered. It is important not to confuse a primitive itself with a variable assigned a primitive value. The variable may be reassigned a new value, but the existing value cannot be changed in the ways that objects, arrays, and functions can be altered.

In JavaScript there are 7 primitive data types:

  • String
  • Number
  • Boolean
  • Null
  • Undefined
  • Symbol
  • BigInt

How to check what kind of primitive data type do we have:

We use The typeof operator to inspect the given value's type:

    typeof undefined === "undefined"; // true
    typeof true === "boolean"; // true
    typeof 42 === "number"; // true
    typeof "42" === "string"; // true
    // added in ES6
    typeof Symbol() === "symbol"; // true
    typeof 37n === "bigint"; // true
    typeof null === "object"; // true
    // Wait what typeof null is object?????
Enter fullscreen mode Exit fullscreen mode

These above-listed types have values of the corresponding type and return a string value of the same name,
except for null which is special in the sense that it seems buggy when combined with the typeof operator:

It would have been nice (and correct!) if it returned "null", but this original bug in JS has persisted for decades and will likely never be fixed because there’s so much existing web content that relies on its buggy behaviour that “fixing” the bug would create more “bugs” and break a lot of web software.

However, If you want to test for a null value using its type, you need a compound condition:

    const a = null;
    (!a && typeof a === "object"); // true
Enter fullscreen mode Exit fullscreen mode

null is the only primitive value that is “falsy” (aka false-like; will be discussed later ) but which also returns "object" from the typeof check.

Going deeper:

The latest ECMAScript standard defines nine types.

Six Data Types that are primitives, checked by typeof operator:

  1. undefined :
typeof instance === "undefined"
Enter fullscreen mode Exit fullscreen mode
  1. Boolean :
typeof instance === "boolean"
Enter fullscreen mode Exit fullscreen mode
  1. Number :
typeof instance === "number"
Enter fullscreen mode Exit fullscreen mode
  1. String :
typeof instance === "string"
Enter fullscreen mode Exit fullscreen mode
  1. BigInt :
typeof instance === "bigint"
Enter fullscreen mode Exit fullscreen mode
  1. Symbol :
typeof instance === "symbol"
Enter fullscreen mode Exit fullscreen mode

Structural Types:

  1. Object :
typeof instance === "object"
Enter fullscreen mode Exit fullscreen mode

Special non-data but Structural type for any constructed object instance also used as data structures: new Object, new Array, new Map, new Set, new WeakMap, new WeakSet, new Date and almost everything made with new keyword;

  1. Function : a non-data structure, though it also answers for typeof operator:
typeof instance === "function"
Enter fullscreen mode Exit fullscreen mode

This is merely a special shorthand for Functions, though every Function constructor is derived from an Object constructor.

 Structural Root Primitive:

  1. null :
typeof instance === "object"
Enter fullscreen mode Exit fullscreen mode

Special primitive type having additional usage for its value:
if an object is not inherited, then null is shown;

Keep in mind the only valuable purpose of typeof operator usage is checking the Data Type. If we wish to check any Structural Type derived from an Object it is pointless to use typeof for that, as we will always receive "object". The proper way to check what sort of Object we are using is the instanceof keyword. But even in that case, there might be misconceptions.

Objects and variables (a must know thing):

Variables:

Variables are containers for storing data (values).

There are 3 ways to declare a JavaScript variable:

  • Using var
  • Using let
  • Using const

The difference between each one of them will be discussed later.

All variables must be identified with unique names, these unique names are called identifiers.
Identifiers can be short names (like x and y) or more descriptive names (age, sum, totalVolume).

The general rules for constructing names for variables (unique identifiers) are:

  1. Names can contain letters, digits, underscores, and dollar signs.
  2. Names must begin with a letter
  3. Names can also begin with $ and _
  4. Names are case sensitive (y and Y are different variables)
  5. Reserved words (like JavaScript keywords) cannot be used as names

The Assignment Operator:

In JavaScript, the equal sign (=) is an "assignment" operator, not an "equal to" operator.
This means if we want to store a primitive data type (or value) we must assign that value to a variable.
To save a number (for example 64) into a variable (let's call it apple) we do this:

const apple = 64;
Enter fullscreen mode Exit fullscreen mode

The "equal to" operator is written like this (==) in JavaScript or like this (===), which of course both has specific use cases which we will talk about in Numbers primitive type later.

Objects:

An object is a value in memory that is possibly referenced by an identifier.

This subject can easily go nasty deep in details, but that’s not what we aim for. We will try to explain in simple words the basic idea.

Objects can be seen as a collection of properties. With the object literal syntax, a limited set of properties are initialized; then properties can be added and removed. Property values can be values of any type, including other objects, which enables building complex data structures. Properties are identified using key values. A key value is either a String or a Symbol value.

There are two types of object properties that have certain attributes: The data property and the accessor property.

  • Data property: Associates a key with a value
  • Accessor property: Associates a key with one of two accessor functions (get and set) to retrieve or store a value.

A JavaScript object is a mapping between keys and values. Keys are strings (or Symbols), and values can be anything.

Functions are regular objects with the additional capability of being callable (as the name suggests functions are containers that have a block of code to be executed when needed)

An object can be created with figure brackets {…} with an optional list of properties. A property is a “key: value” pair, where a key is a string (also called a “property name”), and value can be anything.
To understand this rather abstract definition, let us look at an example of a JavaScript Object:

    // JavaScript code demonstrating a simple object
    const school = {
        name: "ITB school",
        location: "Barcelona",
        established: "2018",
        displayInfo: function() {
            console.log(`${school.name} was established
                    in ${school.established} at ${school.location}`);
        }
    };
    school.displayInfo();
    // output : ITB School was established in 2018 at Bareclona
Enter fullscreen mode Exit fullscreen mode

In the above example “name”, “location”, “established” are all “keys” and “ITB School”, “Barcelona” and 2018 are values of these keys respectively.
Each of these keys is referred to as properties of the object. An object in JavaScript may also have a function as a member, in which case it will be known as a method of that object such as displayInfo key which has a value of a function that can be invoked like earlier with school.displayInfo().

In the above example, “displayinfo” is a method of the school object that is being used to work with the object’s data, stored in its properties.

There is far much more to objects than mentioned before but for now, this is enough to explain our subject.

Primitive wrapper objects in JavaScript:

in JavaScript most things behave like objects even when they aren't objects. For instance, consider how we can call methods on a string (as what we will see later) even though it is primitive and it's supposed to be immutable (cannot be changed):

    console.log("Fred Flintstone".toUpperCase())
    // output : "Fred Flintstone"
Enter fullscreen mode Exit fullscreen mode

How does that work, though? Initially, you might think that strings are objects in disguise and try assigning properties to them.

    const fred = "Fred Flintstone";
    fred.favoriteFood = "Brontosaurus Steak";
    console.log(fred.favoriteFood);
    // undefined
Enter fullscreen mode Exit fullscreen mode

But that doesn't work. And even more strangely, it doesn't trigger an error. It turns out that to allow you to call methods on a primitive, JavaScript does a little bit of trickery which we'll get to shortly.

Apart from null and undefined, all primitive types have a corresponding object equivalent that wraps around the primitive values:

  1. String for the string primitive.
  2. Number for the number primitive.
  3. BigInt for the bigint primitive.
  4. Boolean for the boolean primitive.
  5. Symbol for the symbol primitive.

you can create each by invoking its constructor using the new keyword, The wrapper's valueOf() method returns the primitive value.

    const barney = new String("Barnney Rubble")
    console.log(barney)
    // output : "Barnney Rubble"
Enter fullscreen mode Exit fullscreen mode

But if you try to assign a value to that barney string object wrapper then that would work (strange right):

    barney.favoriteFood = "Pterodactyl Eggs";
    console.log(barney.favoriteFood)
    // output : "Pterodactyl Eggs"

    typeof barney;
    // output : "object"

Enter fullscreen mode Exit fullscreen mode

As you can see, though, the string object can have properties assigned to it, and it reports itself to be of type "object."

The trickery I mentioned before is that any time you attempt to access a property on a primitive, JavaScript will implicitly create a temporary wrapper object. We can verify this by doing the following:

    // this code helps us get the type of the variable during execution
    String.prototype.reportType = function () {
        return typeof this;
    };
    const fred = "Fred Flintstone";

    console.log(typeof fred); // output : "String"
    console.log(fred.reportType()); // output "object"
Enter fullscreen mode Exit fullscreen mode

When we directly check the type of a string primitive we get "string" as expected, but when we check the type of this in a method executed on a string primitive we get "object".

The JavaScript engine doesn't keep this wrapper object around, though. As soon as the work of the method (or other property) is done, it is disposed of.

This explains why trying to assign properties to a primitive doesn't work, but also doesn't throw an error. Assigning the property succeeds, but the property is set on a wrapper object which is immediately destroyed. So, when you go to look up the property later, there is nothing there anymore.

To make things clearer let's take another example:

    const language = 'JavaScript';
    const str = language.toUpperCase();
    console.log(str) // output: 'JavaScript'
Enter fullscreen mode Exit fullscreen mode

In this example, the variable language holds a primitive string value. It doesn’t have any method like toUpperCase() (this method converts all letters into capital). However, the above code works perfectly.

When you call a method on a variable that holds a number, a string, or a boolean, JavaScript performs the following steps behind the scenes:

  • Create an object of a corresponding type.
  • Call a specific method on the instance.
  • Delete the instance immediately.

So the following code:

    const language = 'JavaScript';
    const str = language.toUpperCase();
Enter fullscreen mode Exit fullscreen mode

is technically equivalent to the following code:

    const language = 'JavaScript';
    // behind the scenes of the language.toUpperCase();
    const tmp = new String(language);
    str = temp.toUpperCase();
    temp = null;
Enter fullscreen mode Exit fullscreen mode

Next, we will talk about primitive types in detail. Check out part 2 once it's released and make sure to sign up for our newsletter to be up to date for that matter.

Latest comments (0)