DEV Community

Daniel Li
Daniel Li

Posted on • Originally published at blog.brew.com.hk

(Not) Everything in JavaScript is an Object

This was originally published on The Brewing Press

For those who just wants the answers, feel free to jump to the 'Summary' at the end.

There are a lot of confusion out there as to whether JavaScript is an Object-Orientated programming (OOP) language, or a functional language. Indeed, JavaScript can work as either.

But this lead people to ask "Are everything in JavaScript objects?", "What about functions?"

This post will clear all this up.

Let's start at the start

In JavaScript, there are six primitive data types:

  • Booleans - true or false
  • null
  • undefined
  • number - double-precision 64-bit float. There are no integers in JavaScript.
  • string
  • symbol (new in ES6)

In addition to these six primitive types, the ECMAScript standard also defines an Object type, which is simply a key-value store.

const object = {
  key: "value"
}
Enter fullscreen mode Exit fullscreen mode

So, in short, anything that is not a primitive type, is an Object, and this includes functions and arrays.

All functions are objects.

// Primitive types
true instanceof Object; // false
null instanceof Object; // false
undefined instanceof Object; // false
0 instanceof Object; // false
'bar' instanceof Object; // false

// Non-primitive types
const foo = function () {}
foo instanceof Object; // true
Enter fullscreen mode Exit fullscreen mode

Primitive types

Primitive types have no methods attached to them; so you'll never see undefined.toString(). Also because of this, primitive types are immutable, because they have no methods attached that can mutate it.

You can reassign a primitive type to a variable, but it will be a new value, the old one is not, and cannot, be mutated.

const answer = 42
answer.foo = "bar";
answer.foo; // undefined
Enter fullscreen mode Exit fullscreen mode

Primitive types are immutable

Furthermore, the primitive types are stored as the value themselves, unlike objects, which are stored as a reference. This has implications when performing equality checks.

"dog" === "dog"; // true
14 === 14; // true

{} === {}; // false
[] === []; // false
(function () {}) === (function () {}); // false
Enter fullscreen mode Exit fullscreen mode

Primitive types are stored by value, objects are stored by reference

Functions

A function is a special type of object, with some special properties, such as constructor and call.

const foo = function (baz) {};
foo.name; // "foo"
foo.length; // 1
Enter fullscreen mode Exit fullscreen mode

And just like a normal objects, you can add new properties to the object:

foo.bar = "baz";
foo.bar; // "baz"
Enter fullscreen mode Exit fullscreen mode

This makes functions a first-class citizen, because it can be passed around, as arguments into other functions, just like any other objects could.

Methods

A method is a object property that also happens to be a function.

const foo = {};
foo.bar = function () { console.log("baz"); };
foo.bar(); // "baz"
Enter fullscreen mode Exit fullscreen mode

Constructor Functions

If you have several objects which share the same implementation, you can place that logic inside a constructor function, and then user the constructor function to create those objects.

A constructor function is no different from any other function. A function is used as a constructor function when it is used after the new keyword.

Any function can be a constructor function.

const Foo = function () {};
const bar = new Foo();
bar; // {}
bar instanceof Foo; // true
bar instanceof Object; // true
Enter fullscreen mode Exit fullscreen mode

A constructor function will return an object. You can use this inside the function body to assign new properties to the object. So if we want to make many objects with the property bar initialized to the value "baz", then we can create a new constructor function Foo that encapsulates that logic.

const Foo = function () {
  this.bar = "baz";
};
const qux = new Foo();
qux; // { bar: "baz" }
qux instanceof Foo; // true
qux instanceof Object; // true
Enter fullscreen mode Exit fullscreen mode

You can use constructor functions to create a new object.

Running a constructor function, like Foo(), without new will run Foo like a normal function. this inside the function would correspond to the execution context. So if we call Foo() outside of all functions, it will actually modify the window object.

Foo(); // undefined
window.bar; // "baz"
Enter fullscreen mode Exit fullscreen mode

Conversely, running a normal function as a constructor function would normally return a new empty object, as you have already seen.

const pet = new String("dog");
Enter fullscreen mode Exit fullscreen mode

Wrapper Objects

The confusion arises because of functions like String, Number, Boolean, Function etc. which, when called with new, creates wrapper objects for these primitive types.

String is a global function that creates a primitive string when passed in an argument; it will try to convert that argument into a string.

String(1337); // "1337"
String(true); // "true"
String(null); // "null"
String(undefined); // "undefined"
String(); // ""
String("dog") === "dog" // true
typeof String("dog"); // "string"
Enter fullscreen mode Exit fullscreen mode

But you can also use the String function as a constructor function.

const pet = new String("dog")
typeof pet; // "object"
pet === "dog"; // false
Enter fullscreen mode Exit fullscreen mode

And this will create a new object (often referred to as wrapper object) representing the string "dog", with the following properties:

{
  0: "d",
  1: "o",
  2: "g",
  length: 3
}
Enter fullscreen mode Exit fullscreen mode

Object wrappers are often referred to as wrapper objects, too. Go figure.

Auto-Boxing

What's interesting is that the constructor of both the primitive strings and the object are both the String function. What's even more interesting is the fact that you can call .constructor on the primitive string, when we've already covered that primitive types cannot have methods!

const pet = new String("dog")
pet.constructor === String; // true
String("dog").constructor === String; // true
Enter fullscreen mode Exit fullscreen mode

What's happening is a process called autoboxing. When you try to call a property or method on certain primitive types, JavaScript will first convert it into a temporary wrapper object, and access the property / method on it, without affecting the original.

const foo = "bar";
foo.length; // 3
foo === "bar"; // true
Enter fullscreen mode Exit fullscreen mode

In the above example, to access the property length, JavaScript autoboxed foo into a wrapper object, access the wrapper object's length property, and discards it afterwards. This is done without affecting foo (foo is still a primitive string).

This also explains why JavaScript doesn't complain when you try to assign a property to a primitive type, because the assignment is done on that temporary wrapper object, not the primitive type itself.

const foo = 42;
foo.bar = "baz"; // Assignment done on temporary wrapper object
foo.bar; // undefined
Enter fullscreen mode Exit fullscreen mode

It will complain if you try this with a primitive type which does not have a wrapper object, such as undefined or null.

const foo = null;
foo.bar = "baz"; // Uncaught TypeError: Cannot set property 'bar' of null
Enter fullscreen mode Exit fullscreen mode

Summary

  1. Not everything in JavaScript is an object
  2. There are 6 primitive types in JavaScript
  3. Everything that's not a primitive type is an object
  4. Functions are just a special type of object
  5. Functions can be used to create new objects
  6. Strings, booleans and numbers can be represented as a primitive type but also as an object
  7. Certain primitive types (strings, numbers, booleans) appear to behave like objects because of a JavaScript featured called autoboxing.

Top comments (10)

Collapse
 
lambshift profile image
Juan Pablo de la Torre

Well, actually:

/* This is a zero -> */ 0 /* it could be any number */
// The following defines a function into 0's prototype
.__proto__.pow = function (exponent) {
   return Math.pow(this, exponent)
}

2..pow(5)     // 32
4.5.pow(3)    // 91.125
1.1e2.pow(2)  // 12100
0b100.pow(.5) // 2 > square root
0x1b.pow(1/3) // 3 > cubic root
Enter fullscreen mode Exit fullscreen mode

All of that is valid JS (including the comments between 0 and that period).

Collapse
 
samuraiseoul profile image
Sophie The Lionhart • Edited

Great article.

Remember kiddos,

String("string") == "string";

but

String("string") !== "string";

Collapse
 
bl41r profile image
David Smith • Edited
String("string") == "string";
true
String("string") === "string";
true
String("string") != "string";
false
String("string") !== "string";
false
Enter fullscreen mode Exit fullscreen mode
Collapse
 
samuraiseoul profile image
Sophie The Lionhart

Hrmmm..... I had done basically the same thing in my console when I made my comment, but I had a different result that day. I honestly expected your to be the case but I got what I typed before. I must have had a typo or something. MB

Thread Thread
 
bl41r profile image
David Smith • Edited

I think you forgot the new keyword:

y = "string"
"string"
x = new String("string")
String {0: "s", 1: "t", 2: "r", 3: "i", 4: "n", 5: "g", length: 6, [[PrimitiveValue]]: "string"}
x === y
false
String("string") === "string"
true
new String("string") === "string"
false
typeof(x)
"object"
typeof(y)
"string"
Enter fullscreen mode Exit fullscreen mode
Thread Thread
 
samuraiseoul profile image
Sophie The Lionhart

That's most likely it. You're far more knowledgeable than me in this area it seems. Thanks for adding the clarity for future readers and teaching me something!

Collapse
 
rapasoft profile image
Pavol Rajzak

I am definitely bookmarking this! Thanks

Collapse
 
ben profile image
Ben Halpern

Super practical article, thanks a lot Daniel.

Collapse
 
andy profile image
Andy Zhao (he/him)

Awesome, now I have a cheatsheet. πŸ“ Super helpful for when I practice some JS. Thanks!

Collapse
 
kozlown profile image
Nigel

Excelent article !! :)