All functionality and features introduced in ES6 will be marked with a 6new in ES6. Also, I do not use semicolons when writing Javascript. Mislav Marohnić makes my argument for me here.

It’s a common occurrence while programming to need to verify what types of data you’re working with. Once you know what type of data you’re working with, you know what functions you can call on that data. In Javascript there are many different ways you can find out the datatype of what you’re working with. Here are a few examples with what each returns:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
function Data() {
  this.isData = true
  this.isConstructorFunction = true
}

var data = new Data()

data
/* -> Data {
 *        isData: true,
 *        isConstructorFunction: true,
 *        __proto__: Object
 *    }
 */

data.__proto__
/* -> {
 *        constructor: ƒ Data(),
 *        __proto__: Object
 *    }
 */

Object.getPrototypeOf(data)   // always returns the same as data.__proto__
/* -> {
 *        constructor: ƒ Data(),
 *        __proto__: Object
 *    }
 */

Object.prototype.toString.call(data)
/* -> [object Object]
*/

data.constructor
/* -> ƒ Data() {
 *      this.isData = true
 *      this.isConstructorFunction = true
 *    }
 */


typeof data
/* -> "object"
*/

data instanceof Data
/* -> true
*/

Snippet 1

Before we dive in to what each of these lines is doing and how they’re getting the values that they are, it’s import to understand exactly what types are in Javascript.

Types in Javascript

In Javascript, types can be broken up into 2 different categories: Primitives and Objects.

Primitives are anything that is immutable (i.e. their values can’t be changed) and include the following:

  • Boolean
  • Null
  • Number
  • String
  • Symbol6new in ES6
  • Undefined

Note that, unlike many other languages, strings are immutable in Javascript.

All other datatypes fall under the category of Object and, as we’ll soon see, so do the primitives. This includes, but isn’t limited to, the following built in datatypes (this list is actually fairly extensive and a full version of it can be found on MDN):

  • Object
  • Function
  • Boolean
  • Symbol6new in ES6
  • Error
  • Number
  • Math
  • Date
  • String
  • RegExp
  • Array
  • Promise

The Primitive Datatypes

You’ll notice that some of these built in objects are the same as the primitive datatypes. This is because a primitive datatype can only be a value. It can’t have any methods defined for it or have any properties. Therefore, in order to allow you to do things like:

1
var string = 'this is a string'.split(' ')

Javascript has a built in String object as well as the string primitive datatype. Unfortunately, this leads to some issues when type checking strings, booleans, and numbers. Here is an example of how this can cause trouble:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
// String literal syntax, which involves the primitive datatype
var string1 = 'this is a string'

string1
// -> "this is a string"

Object.getPrototypeOf(string1)
// -> String {length: 0, formatUnicorn: ƒ, truncate: ƒ, splitOnLast: ƒ, contains: ƒ, …}

Object.prototype.toString.call(string1)
// -> "[object String]"

string1.constructor
// -> ƒ String() { [native code] }

typeof string1
// -> "string"

string1 instanceof String
// -> false

// Constructor function syntax, which involves the object datatype
var string2 = new String('this is a string too')

string2
/* -> S{
 *      0: "t",
 *      1: "h",
 *      2: "i",
 *      3: "s",
 *      4: " ",
 *      5: "i",
 *      6: "s",
 *      7: " ",
 *      8: "a",
 *      9: " ",
 *      10: "s",
 *      11: "t",
 *      12: "r",
 *      13: "i",
 *      14: "n",
 *      15: "g",
 *      16: " ",
 *      17: "t",
 *      18: "o",
 *      19: "o",
 *      length: 20,
 *      [[PrimitiveValue]]: "this is a string too"
 *    }     */

Object.getPrototypeOf(string2)
// -> String {length: 0, formatUnicorn: ƒ, truncate: ƒ, splitOnLast: ƒ, contains: ƒ, …}

Object.prototype.toString.call(string2)
// -> "[object String]"

string2.constructor
// -> ƒ String() { [native code] }

typeof string2
// -> "object"

string2 instanceof String
// -> true

Snippet 2

The difference between the 2 syntaxes for creating strings becomes clear when comparing the typeof and instanceof operators, along with the return values of the actual variables. The reason that the lines involving . calls all returned the same thing for both variables is because, in order to call a method or access a property, the primitive strings are converted into objects. This same process happens for the Number and Boolean objects and their corresponding primitive datatypes.

There are also 2 other unintuitive characteristics of the Javascript type system. In addition to the Number primitive type, the NaN and Infinity properties are defined on the global object (read more about the global object on MDN.) These 2 properties both are identified as numbers, despite any implication that they might not be. The reason for this has to do with memory. From javascriptrefined.io:

As Wikipedia states:

Computer arithmetic cannot directly operate on real numbers, but only on a finite subset of rational numbers, limited by the number of bits used to store them.

In ordinary arithmetic, 3.2317006071311 * 10 ** 616 is a real finite number, but, by ECMAScript standards, it is simply too large (i.e, considerably greater than Number.MAX_VALUE), and is therefore represented as Infinity. Attempting to divide an infinity by an infinity yields NaN.

Because Javascript doesn’t allow for numbers over a certain size, it tries to find some other way to represent them. To do this, the “numbers” NaN and Infinity were added. Checking datatype for these works as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
Infinity
// -> Infinity

Object.getPrototypeOf(Infinity)
// -> Number {constructor: ƒ, toExponential: ƒ, toFixed: ƒ, toPrecision: ƒ, toString: ƒ, …}

Object.prototype.toString.call(Infinity)
// -> "[object Number]"

Infinity.constructor
// -> ƒ Number() { [native code] }

typeof Infinity
// -> "number"

Infinity instanceof Number
// -> false

NaN
// -> NaN

Object.getPrototypeOf(NaN)
// -> Number {constructor: ƒ, toExponential: ƒ, toFixed: ƒ, toPrecision: ƒ, toString: ƒ, …}

Object.prototype.toString.call(NaN)
// -> "[object Number]"

NaN.constructor
// -> ƒ Number() { [native code] }

typeof NaN
// -> "number"

NaN instanceof Number
// -> false

Snippet 3

This should look fairly similar to the results of calling these same methods and operators on the primitive string datatype above.

The Object Datatype

Now that we have a better understanding of what the primitive types are in Javascript, we need to look at the more complex ways of differentiating data. I mentioned earlier that everything else in javascript falls under the category of the Object datatype. Objects are Javascript’s way of storing data in memory. This is fundamentally different from how primitive types work. Consider the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/* Varaibles are pointers to locations in memory.
 * By reassigning a variable, we change where it is pointing.
 */
var x = 3
var y = x

x = 4

x    // -> 4

y    // -> 3

/* Since variables point to locations in memory,
 * if we change something at that location,
 * the value of the variable changes.
 */
var arrayX = ['string', 'other string', 2] // Remember arrays are objects too.
var arrayY = arrayX

arrayX    // -> ["string", "other string", 2]
arrayY    // -> ["string", "other string", 2]

arrayX.length = 0

arrayX    // -> []
arrayY    // -> []

Snippet 4

In the first example, x and y are both pointing to the primitive value 3. x is then reassigned to be 4. Since 3 and 4 don’t exist in the same “location” in memory, x and y are no longer pointing at the same value.

In the second example, arrayX and arrayY are both pointing to a specific Array object. That object has the properties 0, 1, and 2 (each with the value of their respective indexes) along with a property length, which holds the number of items in the array. Since x and y are both pointing to the same location in memory, if we change a property of that location, it changes the values of both x and y.

This is the basic usage of Object in Javascript. Despite the simplicity of what an object is, they are powerful tools which you can use to accomplish a wide variety of things. To continue the demonstration of identifying datatypes from earlier:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
var obj = {}

obj
// -> {}

Object.getPrototypeOf(obj)
/* -> {
 *      __defineGetter__: ƒ,
 *      __defineSetter__: ƒ,
 *      hasOwnProperty: ƒ,
 *      __lookupGetter__: ƒ,
 *      __lookupSetter__: ƒ, 
 *      …
 *    }   */

Object.prototype.toString.call(obj)
// -> "[object Object]"

obj.constructor
// -> ƒ Object() { [native code] }

typeof obj
// -> "object"

obj instanceof Object
// -> true

Snippet 5

As you can see, these lines of code operate more like you might expect for objects than they did for the primitive datatypes. However, keep in mind that everything that isn’t a primitive datatype is an object, so there may still be some things that surprise you. For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function fn() {}

fn
// -> ƒ fn() {}

Object.getPrototypeOf(fn)
// -> ƒ () { [native code] }

Object.prototype.toString.call(fn)
// -> "[object Function]"

fn.constructor
// -> ƒ Function() { [native code] }

typeof fn
// -> "function"

fn instanceof Object
// -> true

fn instanceof Function
// -> true

Snippet 6

As you can see, functions are instances of both Functions and Objects. In order to understand how and why that’s the case, we need to explore prototypes and constructors, which we will cover in Part 2.