0

I have always been fascinated by the notion of "prototype" in javascript, and I have used that to model business domains. But I have not used javascript for actual programming until quite recently. So now I am wondering how to really make use thinking in prototypes rather than classes. Recently javascript gained syntactic sugar with class ... extends { constructor() { ... } ... } and all that stuff that makes javascript appear more like Java. But it detracts away from the prototype orientation.

My question is, if my following intuitions jive and what I should read on if I am not interested in just reproducing Java in the browser (got decades of Java under my belt) but rather, how to really use the prototype paradigm.

First thing that I have always admired but never understood is why a class in javascript is implemented as a function?

> new (function(x) { this.foo = x; })(99)
< { foo: 99 }

It is so cool, but why is that? What's the deep philosophy behind calling new on a function, and effectively saying "a class is nothing but a constructor function". What if I call this function without new? Then this is whatever global object and it really doesn't make sense to add foo onto this, or does it? I bet it makes profound sense, but I just don't understand it.

Next there is the question whether I can build a system without ever calling constructors. This is where the prototype oriented programming comes along, which I apply to real business object modeling.

For example, I have a plan for a car,

car = {
  type: "sedan",
  material: "aluminum"
}

which essentially is a model of the car, that is, there is some mapping between the model and a real car. Let's say my plan says it has an aluminum body, then any real world instance of this car also has an aluminum body.

myCar = { plate: "FR-AS-2345" }
myCar.__proto__ = car

so now I can query:

> myCar.plate
< 'FR-AS-2345'
> myCar.type
< 'sedan'

Philosophically, what is a "real car" with respect to an information system anyway? Whether we describe the class of aluminum sedans or my car that's parked just outside, there is always a mapping of real world to information structure. And so, the structure of these car models are no different whether I define a universal aluminum sedan or my particular car.

Where this is really neat is in specification refinement. For example, I can say:

redCar = { color: "red" }
redCar.__proto__ = car

And if my car is actually red:

myCar.__proto__ = redCar

But I could also do something even cooler:

carsLikeMine = { plate = undefined }
carsLikeMine.__proto__ = myCar

so I was able to create a "class" from an instance, saying "just like mine but (of course) with an undefined license plate.

You get the gist. I could use

myCar.__proto__ = redCar

but I could also say

Object.setPrototypeOf(myCar, redCar)

which should I use?

And isn't there some easier way to do that? I was hoping I could somehow use the new operator:

yourCar = new carsLikeMine

but of course that doesn't work. However, I am sitting here pondering why not? I know formally the object is not a "constructor" function which new expects (what makes functions a "constructor" anyway, as I can apply the new operator on any function?) But why should there not be a simple operator some notation where I could say:

yourCar = new extend(carsLikeMine, { plate: "TÜ-PS-1234" })

Oh, so I can make my own prototype inheritance function:

function extend(prototype, object) {
    // perhaps better also clone object first to prevent side-effect
    const newObject = {}
    Object.assign(newObject);
    newObject.__proto__ = prototype;
    return newObject;
}

Why am I the only one making such an extend function? Someone else must have long done this? This whole world of prototype oriented programming must have been explored and charted with all the "no entry" signs posted long ago. What should I read to get on top of that?

And finally, notice that in this prototype approach we do not call "constructors", so any programming that relies on constructors to create valid initialized objects is gone. Should I be concerned about this, or should one just learn to program without relying on constructors? (They are anyway unreliable, because javascript inherently does not protect its objects from change (and neither does Java once you learn to use reflection, I have set private variables on Java objects before).

6
  • 1
    "Why a class in javascript is implemented as a function?" - that has nothing to do with prototypes. It's really just looking cool, and totally confusing if you forget new. Commented Jun 29, 2022 at 11:13
  • 1
    "I could use myCar.__proto__ but I could also say Object.setPrototypeOf, which should I use?" - __proto__ is deprecated, do not use it. Commented Jun 29, 2022 at 11:14
  • 1
    "And isn't there some easier way to do that?" - you're looking for Object.create, which is the real core primitive of prototype inheritance Commented Jun 29, 2022 at 11:15
  • 1
    "Why am I the only one making such an extend function?" - you are not. function extend(p, o) { return Object.assign(Object.create(p), o); }, but most people just inline that. Commented Jun 29, 2022 at 11:16
  • 1
    @Gunther The alternative to constructors (with new) would be factory functions, and ultimately the difference won't be that large. Here's a playground you may like. Commented Jul 2, 2022 at 15:54

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.