Advanced js: Prototype vs. Class
Tags: javascript, webdev, tutorial, beginners
Published: 2022-08-10 12:47:41


Usually we trend to think in terms of _classes_ & _instances_.

Let's do a quick review to `prototype` and `class` theory.

### We use regular functions to create the **illusion** of classes

```js
function foo() {
  console.log('this is just a regular function');
  this.foo = 'foo';
} // « undefined

const f = new foo(); // « Object { foo: 'foo' }
// this is just a regular function

f.foo; // « 'foo'
```

Then, the way of creating _class methods_ is adding a property to foo's `prototype`

```js
foo.prototype.sayHi = () => {
  console.log('hi');
  return 1;
}; // « function sayHi()

f; // « Object { foo: 'foo' }

f.sayHi(); // « 1
// hi
```

This is so _standard_ that ES5 (and `ts` before them) introduced syntactic sugar:

```js
class goo {
  constructor() {
    this.goo = 'goo';
  }
  sayHi() {
    console.log('hi');
    return 1;
  }
} // « undefined

g = new goo(); // « Object { goo: "goo" }
g.sayHi(); // « 1
// hi
```

***

## The truth is...

There are no classes. Neither instances. While in class-oriented languages, multiple copies (instances) of a class can be made, like stamping or copying behavior out from a mold. In `js` we have just regular friendly objects.

## ...`js` is prototype-oriented.

What we're truly doing on the examples above is creating a new object and **linking** them to another **plain**, **regular** object. 

This linked object is the `prototype`.

We are linking each new object to the `prototype` of the function we've called. 

### There are plain js-objects in memory; no more, no less.

```js
function foo() {
  this.foo = 'foo';
}
foo.prototype.sayHi = () => console.log('say hi');

f = new foo();

/*

 -------------  linked to   -----------------  linked to  ------------------  linked to
| foo: string | ---------> | sayHi: Function | ---------> | { toString... } | ---------> null
 -------------              -----------------             ------------------
  ^                           ^                              ^
  f                          foo.prototype                 Object.prototype

*/
```

```js
foo.prototype; // Object { sayHi: Function }
Object.getPrototypeOf(f) == foo.prototype; // « true

Object.prototype; // « Object { ... }
Object.getPrototypeOf(foo.prototype) == Object.prototype; // « true

Object.getPrototypeOf(Object.prototype); // « null
```

Thing is, IMO this is even more powerful (and elegant) than classic instances. Since everything are in-memory objects, we could even modify the thing in execution time:

```js
function player() {}
player.prototype.sayHi = () => console.log('hi');

a = new player();
b = new player();
a.sayHi(); // « 'hi'
b.sayHi(); // « 'hi'

// we can modify the behaviour in execution time

player.prototype.sayHi = () => console.log('hello');

a.sayHi(); // « 'hello'
b.sayHi(); // « 'hello'
```

## Applying the theory

With this in mind. We (our team) challenge to stop thinking in terms of _classes & instances_, and start thinking on _prototype objects_.

Imagine you need two objects, `o1` and `o2`, each one overriding some default behavior/values. This is how we'd code that:
 
```js
const defaultBehaviour = {
  name: 'guest',

  sayHi() {
    return 'hi ' + this.name;
  },

  foo() {
    return 42;
  },
};

const o1 =  Object.setPrototypeOf({ name: 'Jane Doe' }, defaultBehaviour);

o1.sayHi(); // « hi Jane Doe
o1.foo(); // « 42

const o2 = Object.setPrototypeOf({ foo: () => 100 }, defaultBehaviour);

o2.sayHi(); // « hi guest
o2.foo(); // « 100
```

While the class sugar-syntax is perfectly valid. Let's remove the mask and claim the true behavior of `js` 🦾

--

I can't recommend hard enough to read [Kyle Simpson](https://me.getify.com/). 

--

Cover image from [undraw.co](https://undraw.co/)

Thanks for reading 💚.