This is a story about one very special piece of JavaScript, the
most used artificial language in the world today (2019).
The article presents a kind of philosophical view of Inheritance in JavaScript, and I dare only hope that it is based on the most impressive source of knowledge: life itself in all its manifestations. I don’t know if this was an inspiration when creating the prototype chain design in JavaScript.
But if so, then it is so significant and powerful that when I start thinking about it, sometimes it becomes even difficult to breathe ...
(all links are underlined )
I’m also sure that none of us will doubt that
Brendan Ike (Eich) - the author of the JavaScript programming language - is an outstanding genius! And not only because he often repeats:
Always bet on JavaScript!
Let's start! And our first starting point will be Imagination, in which we first turn off all prejudices, omissions and other side effects.
We are heading
Back to the Future , the era preceding the creation of the modern Internet in the early 1990s.
From the time of the
first Hackers who invented everything that we (
IT-employees ) are now
using , we have been moving to an impressive sketch: about the war between the browsers
Netstcape Navigator 2 and
Internet Explorer 3.
Java has just come out, and almost everything from the modern Internet has not yet been invented or not pre-open. It is possible that, like me, in those “good old days” you were young, and you could remember this wonderful feeling of involvement in all the splendor that is being created right before your eyes.
So, you have a very powerful
PC on the most modern
Intell Pentium 200 MMX with 32Mb of RAM, Windows 3.11 or even Windows 95 and you are looking to the future with hope! And, of course, both
browsers are also installed. You have a
Dial-Up , through which you connect to the Web for new junk, too, for study or just to chat, chat. Although, hey, you still can’t chat directly in the browser, most likely you are using delayed message delivery systems, something like
EMail or maybe
UseNet , or, quite possibly, you have already mastered instant delivery via
IRC .
A couple of years pass and literally EVERYTHING changes ... Suddenly you watch the animation of snowflakes on the web-pages congratulating you on New Year and Christmas. Of course, you are interested in how this is done, and you discover a new language - JavaScript. Since
HTML is no longer new to you, you begin to learn this alluring techno-craft. Soon you discover
CSS and it turns out that this is also important, since everything is now made from a combination of these three: HTML, JavaSript and CSS. Wow.
Around the same time, you might notice a couple of great things in Windows itself,
CScript and
HTA appeared in it, and even then it became possible to create full-fledged desktop applications directly on JS (and this still works).
And so you started making your first Web-Server, possibly in
Perl or
C ~
C ++ . Perhaps even you started using
Unix-like OSes and do it on
bash . And all of it "spinning-spinning" thanks to the
Common Gateway Interface (do not confuse it with that other
CGI ).
PHP also almost does not exist yet, but maybe you will like it soon.
200x era. You are now doing
ASP in
JScript . This is very similar to the JavaScript that works inside your web pages. It's so cool! You are considering creating your own
template engine , a kind of parody of
XML . And then someone suddenly calls
AJAX all these fun ways to
dynamically load content that you've been using for several years now. And all of them now think that there is only
XMLHTTPRequest , but you remember that the data can be transferred to
BMP ,
IFrame or even by inserting the
<script> tag . And then someone suddenly enthusiastically talks about
JSON and how useful it is, when you have been driving data for an
eternity with something like this:
document.write("<" + "script src=" + path + ">");
It is all not that matter
now , but you still can remember
how ...
When you come to your senses, from time to time you begin to find yourself poking around with
Rhino or
Nashorn in attempts to satisfy the wishes of Java customers using
Alfresco or
Asterisk . And you've already heard about the imminent advent of JavaScript in the world of microchips, and are very inspired by this news. And of course, now you have
jQuery and
Backbone .
Watching the snowfall of the coming 2010, you already know that in your world all the rules of the game will soon change, as “Player No. 1” has entered the field:
Node.js. And the next 10 years you will spend with this new toy, and even now, in 2019, you still can’t get enough of how cool it is.
In general, you are happy with everything, everything suits you, all these toys and games in them make up a vast part of your life interests.
But there is one small question that you ask yourself day after day, night after night for two decades now:
If you had to do this, how would you explain Empathy using JavaScript?
You know that one of the most complicated topics in JavaScript is
Prototype Inheritance and the Prototype Chain . And you love this topic, you can explain how it all works and works, simply because you learned it almost from the very beginning, even before the
first version of the Standard was born, and where, as you remember, there are
4.2 .1 Objects :
ECMAScript supports prototype-based inheritance. Every constructor has an associated prototype, and every object created by that constructor has an implicit reference to the prototype (called the object's prototype) associated with its constructor. Furthermore, a prototype may have a non-null implicit reference to its prototype, and so on; this is called the prototype chain .
Each object is created with an implicit reference to the prototype. This is called the prototype inheritance chain, which you can continue for as long as you like.
Wow ... And if suddenly, like me, you might be thinking that this is one of the greatest inventions in
Compuer Science , then how would you express the effect that reading this statement had on you?
Let's go back to the beginning. In the yard 1995. You are Brendan Ike, and you
need to invent a new programming language. Perhaps you like
Lisp or
Scheme , at least some of their favorite parts. And you are faced with the need to solve the problem of
Inheritance , since you must pretend that the language has a certain implementation of
OOP .
Let’s think : you need to mix all the things that you like, maybe some things that you don’t like, and the resulting cocktail should be Good enough so that no one will notice the catch until there is a real need to understand how it is arranged inside.
And now the question is:
what would happen to Inheritance?
Let's get back to reality for a moment. What do we all know about Inheritance? Some obvious pieces of answers to this question:
- Most living forms are based on the Genome . It is such a repository of information about the likely characteristics and alleged behavior of living things. When you are a living creature, you carry a part of the genome within you, you can distribute it, and you received it from previous generations.
- You can create a living creature using two techniques: mix (the genomes) of two ancestors or, possibly, use the monoecious cloning of one of them. Of course, today we are available technologies that allow mixing the genomes of more than one creature, but this is much less obvious and not so natural.
- The time factor is important. If there is no inherited property, or not already, then our only way out is to create it from scratch. In addition to this, there is also the Legacy, that which passes to the being from its ancestors not through the genome, but through the laws of property, and this can also be significant.
Going back to the past, and the right question that we need to ask ourselves is now:
And, in fact, inheritance What do we want to get?
Well, in any case, in the process of solving the problem of inheritance, we need to at least fill the gap between programming and real life, otherwise, in general, we will not have any right to call it Inheritance.
And in general, we will “finish ourselves up”: we are in the 1995s, and we have a powerful PC with only 32 megabytes of RAM, and we are trying to create an interpreted language, so we have to take great care of this memory. Each piece of data, especially
String objects , consumes a lot of memory, and we should be able to declare this piece only once, and whenever possible in the future always use only pointers to the memory area occupied by this piece.
In general: a very difficult question.
There is a popular opinion: "
JavaScript is created from objects ." Using this triviality, we can very easily answer the question “
from what ” to inherit and “
what ”: from Objects to Objects. For the sake of the issue of saving memory, it turns out that all the data should be stored in these objects, and all these links to the data should also be stored in the Inherited properties of these objects. Perhaps now it’s clear why in 1995 we really might need to create a Design based on a prototype chain: it saves memory as long as possible! And in general, I think that this can still be a very important aspect.
Based on the indicated design and the opinion "
everything is an Object ", we can try to clone something like that. But what is cloning here now? I think that following the requirements of our requirements, we can assume something like
Structural or Surface Copy , in something similar to the predecessors of modern
Object.assign .
Let's implement a simple structural copy in 1995 using
for (var i in) {} , since the standard
already allowed this :
As you can see, this approach still “works”, although in general, of course, I would recommend looking at the
deep-extend module for a more detailed understanding of how to make cloning in JavaScript, but for the purposes of the article, consistent application is quite suitable for us described by
cloneProps , because we could well use it in those ancient times:
- cloning Objects using the Constructor: using the constructor, create at least two different clones
- cloning the Constructor from the Constructor: we implement the use of the behavior of one constructor from another constructor
var SomeConstructor = function () { this.a = 'cloned'; }; var AnotherConstructor = function () {
- cloning a Constructor using an Object: we will use the same object to implement cloning in at least two constructors
var existentObject = { foo : 'bar' }; var SomeConstructor = function () { cloneProps(foo, this); }; var OtherConstructor = function () { cloneProps(foo, this); };
- cloning one Object from another Object: use one object to create several of its clones . There is nothing to describe here, we just take it as is our cloneProps from the first example above.
With cloning, in general, everything is simple, as we see, everything is clear and in general, but ...
Is it so easy for us to do the Inheritance of Entities, using a combination of their predecessors?
- Inheritance of an object using the Constructor: this is the very purpose of the constructors, just show how it is originally designed .
var existentObject = { foo : 'bar' }; var SomeConstructor = function () {}; SomeConstructor.prototype = existentObject; var inheritedObject = new SomeConstructor();
- Inheritance of the Constructor from another Constructor: no doubt, the first who noticed this was an outstanding Genius. All in all, this is another classic example from everywhere .
var FirstConstructor = function () { this.foo = 'bar'; }; var InheritedConstructor = function () { FirstConstructor.call(this); }; InheritedConstructor.prototype = { bar : 'foo' }; InheritedConstructor.prototype.constructor = FirstConstructor; var inherited = new InheritedConstructor();
it would be possible to state something sophisticated, but why
- Inheritance of the Constructor from the Object: again, we just use .prototype = object every time we need it, there is nothing to describe, we always need to assign Constructor.prototype since it is supposed to put the object there, and by an implicit link we get all its properties .
- Inheriting an Object from an Object: the same thing. We just put the first object in Constructor.prototype and as soon as we say new Constructor we will create an inherited copy in which there will be implicit references to the properties of our first object.
And of course, in all these situations with inheritance, we will have the opportunity to check using
instanceof which constructor we created the objects from, although, of course, it should be noted that
instanceof itself appeared in the standards almost four years later.
True, there remained such a small detail from paragraph 4.2.1:
be able to do this for as long as necessary, as it says:
and so on
Well,
let's try to make inheritance truly endless , using technology
from 1995 .
In fact, let's imagine that we have two Entities, and
Not Constructors, but simple objects. And we wanted to inherit one from the other, and then maybe from the other, and from the other, and so on ...
But how?
Take a look a little further, deeper.
The correct answer here again is:
Inheritance of what we need to create?
After all, we do not need these Entities on our own. We need their properties: associated data consuming memory; and also, we probably need some behavior: methods using this data. And it will be just as
honest , if we have the opportunity to check where and where we inherited and using what. In general, it will be just as cool if we could Reproduce the inherent design of the Inheritance patterns in the future, implying that if we inherit one from the other many times, we will always get the same result, according to what we wrote (contract) . Although it’s still, it can be just as useful for us to somehow fix the moment of creation, since our “previous” entities can change over time, and the “heirs”, giving them respect in this, still change with them may not want to.
And, since all of our code is a combination of data and behavior, will it be generally normal to use the same method — combining data and presentation — when designing an Inheritance system?
As for me, all this resembles what we see when observing Life in all its incredible forms. From the first unicellular, to multicellular and their descendants, and further to Animals, people, humanism and tribes, civilizations, Intellect, and its artificial forms, and further to Space, to the Galaxy, to the Stars! and:
“... All we need to do is make sure we keep talking ...”
(all we need to do is continue communication)
Incredible in its thoughtfulness, a quote from
Stephen Hawking , subsequently popularized in
this Pind Floyd masterpiece.
Programming languages ​​based on
messaging and a
Flow-based concept implemented through a robust internal API allow you to move from
simple data to higher abstractions for a description and everything else. I think this is pure Art, and how it works in particular in deeply hidden JavaScript structures through implicit relationships between data in prototype chains.
Imagine again two ancestors, they communicate and at one moment their emotions and feelings create a child. The child grows up, meets another child, they communicate, and the next descendant appears, and then on and on and on ... And we always need Two parents, otherwise it is not natural, it will already be genetic engineering. Two, no more and no less. A descendant receives the same as their Legacy, so it’s simple and clear.
I understand it will sound strange, but yes, we have everything we need to create this model of Inheritance in 1995. And the basis of all this is precisely
4.2.1 Objects , implicit referencing through prototype.
And that’s exactly it, as it is, combining
ParentObject with
ParentConstructor by specifying
.prototype and then
Constructor will probably create us a ChildObject if we say the magic word “
new ”:
var ParentObject = { foo : 'bar' }; var ParentConstructor = function () {}; ParentConstructor.prototype = ParentObject; var ChildObject = new ParentConstructor();
We can discern here both of our ancestors. At the moment when we said the magic word "
new " we asked them to chat. If they do not want to communicate, Life will stop, the process will fall with an error, and the
compiler (interpreter) will tell us about it.
Of course, yes, but we did ask for the
Inheritance Tree, or let it be obviously much simpler, at least for the
Genealogical Tree. And the answer is still the same ... our
Child Object grows up, and it
becomes a Parent Object , then it meets a new
Constructor Object and as soon as we say the coveted word "
new " - magic:
And we can continue to do this ad infinitum. And I, perhaps, really believe that this was the main Idea when developing the design of the Prototype Chain, because as we all know, this approach creates some very neat but no less unpleasant problems ...
1: Community ... As you can easily check for yourself, specifying in
.prototype ParentConstructor ' a or
AnotherConstructor' a is a very serious and strict
Social Contract in our Tribe. It creates a reference to the
ParentObject (
.foo ) properties for the Heirs:
ChildObject and
SequentialChildObject . And if we get rid of this indication, then these links will disappear. If we contrive, and reassign this reference to some other object, then our heirs will instantly inherit other properties. Therefore, combining ancestors through
.prototype , we could probably say that we are creating some kind of
cell of society , because these “ancestors” can reproduce many identical offspring every time we ask them about it using
new . And thus, having destroyed the "family", we are ruining the hereditary qualities of his descendants, such a drama; ^)
Perhaps this is all talk about
Legacy in our code, we should take care of this when we are going to create a
safe and
supported code! Of course, there was no question of any
SOLID ,
Liskov Principle and
Design by Contract and
GRASP in 1995, but it’s obvious that these methodologies were not created “from scratch”, it all started much earlier.
2: Family ... We can easily verify that our
ParentObject is allowed to be very frivolous with other Constructors. This is not fair, but we can use as many Constructors as we wish in the Inheritance of our ParentObject and, thus, create as many families as we like. On the other hand, each Constructor is very closely associated with ParentObject through the .prototype task, and if we do not wish to harm our heirs, we must maintain this connection for as long as possible. We could call it the art of tragedy in the history of our tribe. Although, of course, this protects us from Amnesia - the forgetfulness of what we inherited and from whom, and why our heirs get such
Legacy . Glory to
Mnemosyne !, We really can easily test our Prototype Chain
Tree and find the
Artifacts of what we did wrong.
3: Old age ... Our
ParentObject and
Constructor are certainly susceptible to damage during the life cycle of our program. We can try to take care of this, but no one is safe from mistakes. And all these changes can harm the Heirs. We must take care of
memory leaks . Of course, we can very well destroy unnecessary parts of the code in runtime and thus free up memory that is no longer used in our
Life Cycle . In addition, we must get rid of all the possibilities of creating
Temporary Paradoxes in the chains of prototypes, since in fact it is quite simple to inherit the Ancestor from his own descendant. But this can already be very dangerous, since such techniques of flirting with the past from the future can create whole heaps of difficultly reproducing
Heisenbags , especially if we try to measure something that can itself change over time.
Chronicle of Decisions
Let it be simple, obvious and not very useful, but instead of thinking about our Designer and ParentObject as Mom and Dad, let's describe them as an
egg and ...
pollen . Then, in the future, when we create the
Zygote using the treasured word "
new " it will no longer do any harm to our Imagination!
The moment we do this, we will immediately get rid of all three of the above problems! Of course, for this we need the Factory of Designers. Call them what you want, mothers, fathers, what’s the difference, because the bottom line is that if we are going to say “
new ”, then we have to create a “brand new” designer flower cell, put pollen on it and this will allow us to grow New Flower in 1995:
var Pollen = { season : 'Spring' };
(and yes, do not forget to check the season with this snowdrop)
Of course, the Cyclomatic Complexity of the solutions that you try to create using this approach will be quite comparable to Einstein's Riddle . Therefore, I threw the library , it can help with the creation of chains of designers and memoization , (editor's note: well, takoe, take a pie from the shelf, bla-bla-bla ) ...
And although I can not prove it, but this approach is quite successful it has been used from time to time for two decades now, if it is necessary to be 146% sure that everything is normal with inheritance. You can easily see for yourself that it is elementarily tested, reproduced and supported (Ed.:Yeah, right now, they left everything and went to make sure ).
Of course, this is not the whole story, we simply stated the fact: JavaScript is designed well enough to describe the Genealogy Graph directly through Inheritance. Of course, here we did not slyly touch on the topic of Class degradation, but I’m sure you yourself can easily replace FlowerEggCell with FlowerEggCellClass inside FlowersFactory : the essence will remain the same, if you want to check your flowers through instanceof you will see that they are all descendants of FlowerEggCell to which you refer via FlowerZygote . And of course now you can change the properties of theFlowerZygote , because it will not do any harm to FlowersFactory itself, it will remain able to create other designers in the future according to the design that you originally laid down.
I hope that this article has dispelled all doubts about the importance of words .prototype and next time when you see a null in the place where he would have been the this , for example . call (null , . the apply (null or . the bind (null you will experience sadness of knowing how their code style poor design (approx. Ed .: but Sorrowfind and listen, there is also on YouTube, it is good, there are even a couple of correct lines about broken promises ).
Thanks for reading!
See you soon!
Sincerely your V