Methods of primitives

JavaScript allows to work with primitives (strings, numbers etc) as if they were objects.

They also provide methods to call and such. We are going to study them soon, but first let’s see how it works, because, of course, primitives are not objects (and here we plan to make it even more clear).

Let’s formulate the key distinction between primitives and objects.

A primitive
Is a value of a primitive type. There are 6 primitive types: string, number, boolean, symbol, null and undefined.
An object
Is capable of storing multiple values as properties. Can be created with {}, for instance: {name: "John", age: 30}. There are other kinds of objects in JavaScript, e.g. functions are objects.

One of the best thing about objects is that we can store a function as one of properties:

let john = {
  name: "John",
  sayHi: function() {
    alert("Hi buddy!");
  }
};

john.sayHi(); // Hi buddy!

So, here we’ve made an object john with the method sayHi.

There exist many built-in objects, including those that work with dates, errors, HTML elements etc. They have different properties and methods.

But features come at a price!

Objects are “heavier” than primitives. They require additional resources to support the internal machinery. But properties and methods are useful in programming, JavaScript engines try to optimize them, so the price is usually fair.

A primitive as an object

Here’s the paradox faced by the creator of JavaScript:

  • There are many things one would want to do with a primitive like a string or a number. Could be great to access them as methods.
  • Primitives must be as fast and lightweight as possible.

The solution looks a little bit awkward, but here it is.

  1. Primitives are still primitive. A single value, as desired.
  2. The language allows to access methods and properties of strings, numbers, booleans and symbols.
  3. When it happens, a special “object wrapper” is created that provides the functionality and then is destroyed.

The “object wrappers” are different for each primitive type and are named specifically: String, Number, Boolean and Symbol. Thus they provide different sets of methods.

For instance, there exists a method str.toUpperCase() that returns the capitalized string.

Here’s how it works:

let str = "Hello";

alert( str.toUpperCase() ); // HELLO

Simple, right? And here’s what actually happens in str.toUpperCase():

  1. The string str is a primitive. So in the moment of accessing its property a special object is created that both knows the value of the string and has useful methods, like toUpperCase().
  2. That method runs and returns a new string (shown by alert).
  3. The special object is destroyed, leaving the primitive str alone.

So, primitives can provide methods, but they still remain lightweight.

Of course, a JavaScript engine highly optimizes that process. Internally it may skip the creation of the extra object at all. But it must adhere to the specification and behave as if it creates one.

A number has methods of it’s own, for instance, toFixed(n) rounds the number to the given precision:

let n = 1.23456;

alert( n.toFixed(2) ); // 1.23

We’ll see more specific methods in chapters Numbers and Strings.

Constructors String/Number/Boolean are for internal use only

Some languages like Java allow to create “wrapper objects” for primitives explicitly using syntax like new Number(1) or new Boolean(false).

In JavaScript that’s also possible for historical reasons, but highly not recommended. Things will go crazy in many places.

For instance:

alert( typeof 1 ); // "number"

alert( typeof new Number(1) ); // "object"!

And, because zero is an object:

let zero = new Number(0);

if (zero) { // zero is true, because it's an object
  alert( "zero is truthy?!?" );
}

From the other side, using same functions String/Number/Boolean without new is a totally sane and useful thing. They convert a value to the corresponding type: to a string, a number, or a boolean (primitive).

This is totally valid:

let num = Number("123"); // convert a string to number
null/undefined have no methods

Special primitives null and undefined are exceptions. They have no corresponding “wrapper objects” and provide no methods. In a sense, they are “the most primitive”.

An attempt to access a property of such value would give an error:

alert(null.test); // error

Summary

  • Primitives except null and undefined provide many helpful methods. We plan to study those in the next chapters.
  • Formally, these methods work via temporary objects, but JavaScript engines are very well tuned to optimize that internally, so they are not expensive to call.

Tasks

importance: 5

Consider the following code:

let str = "Hello";

str.test = 5;

alert(str.test);

How do you think, will it work? What will be shown?

Try running it:

let str = "Hello";

str.test = 5; // (*)

alert(str.test);

There may be two kinds of result:

  1. undefined
  2. An error.

Why? Let’s replay what’s happening at line (*):

  1. When a property of str is accessed, a “wrapper object” is created.
  2. The operation with the property is carried out on it. So, the object gets the test property.
  3. The operation finishes and the “wrapper object” disappears.

So, on the last line, str has no trace of the property. A new wrapper object for every object operation on a string.

Some browsers though may decide to further limit the programmer and disallow to assign properties to primitives at all. That’s why in practice we can also see errors at line (*). It’s a little bit farther from the specification though.

This example clearly shows that primitives are not objects.

They just can not store data.

All property/method operations are performed with the help of temporary objects.

Tutorial map

Comments

read this before commenting…
  • You're welcome to post additions, questions to the articles and answers to them.
  • To insert a few words of code, use the <code> tag, for several lines – use <pre>, for more than 10 lines – use a sandbox (plnkr, JSBin, codepen…)
  • If you can't understand something in the article – please elaborate.