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).
new.myCar.__proto__but I could also sayObject.setPrototypeOf, which should I use?" -__proto__is deprecated, do not use it.Object.create, which is the real core primitive of prototype inheritancefunction extend(p, o) { return Object.assign(Object.create(p), o); }, but most people just inline that.