The true prototypial nature beneath "JavaScript classes"
Tags: javascript, typescript
Published: 2020-07-26 11:18:46


I first wrote this [stackoverflow answer](https://stackoverflow.com/questions/10430279/extending-an-object-in-javascript/28281845#answer-28281845) in 2015. Obviously things has changed quite a bit, but still think there are much misdirections in JavaScript to address. 

This article, as its title would suggest, will be contentious. But please, I am not about to say that we shouldn't use `class` and `new`. But to make that little dent, catch your attention, and hopefully we can all have some discussions over it.

Mainly is to explore, through a simple syntax, that Javascript is inherently classless, and its powerful prototypial nature obscured by `class` and `new`. 

**But**, on balance, you have much to gain, and nothing to lose using ES6 classes (provided one writes it readably). 

The point at the end of the day, please think of readability. The closer a language looks like to a human language, the better.
# World without the "new" keyword.
### And simpler "prose-like" syntax with Object.create().

**First off, and factually, Javascript is a [prototypal language](https://alistapart.com/article/prototypal-object-oriented-programming-using-javascript/)**, not class-based. The `class` keyword is in fact is just prototypial under the hood. Indulge me, and have a look at its true nature expressed in the simple prototypial form below, which you may come to see that is very simple, prose-like, yet powerful. I also will not use the `prototype` property, because I also find it rather unnecessary and complicated.

### TLDR;

```js
const Person = { 
    firstName: 'Anonymous',
    lastName: 'Anonymous',
    type: 'human',
    name() { return `${this.firstName} ${this.lastName}`},
    greet() { 
        console.log(`Hi, I am ${this.name()}.`)
    } 
}
    
const jack = Object.create(Person) // jack is a person
jack.firstName = 'Jack'            // and has a name 'Jack'
jack.greet()                       // outputs "Hi, I am Jack Anonymous."
```
**This absolves the sometimes convoluted constructor pattern.** A new object inherits from the old one, but is able to have its own properties. If we attempt to obtain a member from the new object (`#greet()`) which the new object `jack` lacks, the old object `Person` will supply the member. 

In [Douglas Crockford's words: "Objects inherit from objects. What could be more object-oriented than that?"](https://www.crockford.com/javascript/prototypal.html)

You don't need constructors, no `new` instantiation ([read why you shouldn't use `new`](https://stackoverflow.com/questions/8202889/why-does-crockford-say-not-to-use-the-new-keyword-if-he-advises-us-to-use-it-for)), no `super`, no self-made `__construct`, no `prototype` assignments. You simply create Objects and then extend or morph them.

This pattern also offers [immutability (partial or full)](https://medium.com/better-programming/javascript-prevent-objects-from-being-changed-d1ca82f02975), and [getters/setters](#on-raw-objectdefineproperty-endraw-).

### TypeScript Equivalent

The TypeScript equivalent requires declaration of an interface:

```ts
interface Person { 
    firstName:  string,
    lastName: string,
    name: Function,
    greet: Function
}

const Person = { 
    firstName: 'Anonymous',
    lastName: 'Anonymous',
    name(): string { return `${this.firstName} ${this.lastName}`},
    greet(): void { 
        console.log(`Hi, I am ${this.name()}.`)
    } 
} 
const jack: Person = Object.create(Person)
```

## Creating an descendant/copy of `Person`

>*Note: The correct terms are `prototypes`, and their `descendants/copies`. There are no `classes`, and no need for `instances`.*

```js
const Skywalker    = Object.create(Person)
Skywalker.lastName = 'Skywalker'

const anakin       = Object.create(Skywalker)
anakin.firstName   = 'Anakin'
anakin.gender      = 'male' // you can attach new properties.

anakin.greet() // 'Hi, my name is Anakin Skywalker.'
```
Let's look at the prototype chain:
```js
/* Person --> Skywalker --> anakin */
Person.isPrototypeOf(Skywalker) // outputs true
Person.isPrototypeOf(anakin)    // outputs true
Skywalker.isPrototypeOf(anakin) // outputs true
```
If you feel less safe throwing away the constructors in-lieu of direct assignments, fair point. One common way is to attach a `#create` method which you are read more about [below](#commonly-attached-raw-create-endraw-method).

## Branching the `Person` prototype to `Robot`

**Say when we want to branch and morph:**
```js
// create a `Robot` prototype by extending the `Person` prototype
const Robot = Object.create(Person)
Robot.type  = 'robot'
Robot.machineGreet = function() { console.log(10101) }

// `Robot` doesn't affect `Person` prototype and its descendants
anakin.machineGreet() // error
```
And the prototype chain looks like:
```js
/*
Person ----> Skywalker --> anakin
        |
        |--> Robot
*/
Person.isPrototypeOf(Robot) // outputs true
Robot.isPrototypeOf(Skywalker) // outputs false
```
**...And Mixins -- Because.. is Darth Vader a human or robot?**
```js
const darthVader = Object.create(anakin)

// for brevity, skipped property assignments 
// you get the point by now.

Object.assign(darthVader, Robot)

// gets both #Person.greet and #Robot.machineGreet
darthVader.greet() // "Hi, my name is Darth Vader..."
darthVader.machineGreet() // 10101
```

**Along with other odd things:**

```js
console.log(darthVader.type)     // outputs "robot".
Robot.isPrototypeOf(darthVader)  // returns false.
Person.isPrototypeOf(darthVader) // returns true.
```

**Which elegantly reflects the "real-life" subjectivity:**

> "He's more machine now than man, twisted and evil." - Obi-Wan Kenobi

> "I know there is good in you." - Luke Skywalker

**In TypeScript you would also need to extend the `Person` interface:**
```ts
interface Robot extends Person {
    machineGreet: Function
}
```

## Conclusion

I have no qualms with people thinking that `class` and `new` are good for Javascript because it makes the language familiar and also provides good features. I use those myself. The issue I have is with people extending on the aforementioned basis, to conclude that `class` and `new` is just a semantics issue. It just isn't. 

It also gives rise to tendencies to write the simple language of Javascript into classical styles that can be convoluted. Instead, perhaps we should embrace:

1. `class` and `new` are great syntactic sugar to make the language easier to understand for programmers with class languages background, and perhaps allows a structure for translating other other languages to Javascript.
2. But under the hood, Javascript is prototypial.
3. And after we have gotten our head around Javascript, to explore it's prototypial and more powerful nature.

Perhaps in parallel, it should allow for a `proto` and `create` keyword that works the same with all the ES6 classes good stuff to absolve the misdirection.

Finally, whichever it is, I hoped to express through this article that the simple and prose-like syntax has been there all along, and it had all the features we needed. But it never caught on. ES6 classes are in general a great addition, less my qualm with it being "misleading". Other than that, whatever syntax you wish to use, please consider **readability**.

### Further reading

#### Commonly attached `#create` method

Using the `Skywalker` example, suppose you want to provide the convenience that constructors brings without the complication:
```js
Skywalker.create = function(firstName, gender) {

    let skywalker = Object.create(Skywalker)

    Object.assign(skywalker, {
        firstName,
        gender,
        lastName: 'Skywalker'
    })

    return skywalker
}

const anakin = Skywalker.create('Anakin', 'male')
```

### On `#Object.defineProperty`
For free getters and setters, or extra configuration, you can use Object.create()'s second argument a.k.a propertiesObject. It is also available in [#Object.defineProperty](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty), and [#Object.defineProperties](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperties). 

To illustrate its usefulness, suppose we want all `Robot` to be strictly made of metal (via `writable: false`), and standardise `powerConsumption` values (via getters and setters).

```js
const Robot = Object.create(Person, {
    // define your property attributes
    madeOf: { 
        value: "metal",
        writable: false,
        configurable: false,
        enumerable: true
    },
    // getters and setters
    powerConsumption: {
        get() { return this._powerConsumption },
        set(value) { 
            if (value.indexOf('MWh')) {
                this._powerConsumption = value.replace('M', ',000k')
                return 
            }
            this._powerConsumption = value
            throw Error('Power consumption format not recognised.')
        }  
    }
})

const newRobot = Object.create(Robot)
newRobot.powerConsumption = '5MWh'
console.log(newRobot.powerConsumption) // outputs 5,000kWh
```

**And all prototypes of `Robot` cannot be `madeOf` something else:**

```js
const polymerRobot = Object.create(Robot)
polymerRobot.madeOf = 'polymer'
console.log(polymerRobot.madeOf) // outputs 'metal'
```