JavaScript Inheritance from the Point of View of a Bored Nerd: Constructors Factory

lamp of light and apple of discord 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:



  1. 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.
  2. 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.
  3. 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 :



 // back in 1995 cloning // it is not deep clone, // though we might not need deep at all var cloneProps = function (clonedObject, destinationObject) { for (var key in clonedObject) { destinationObject[key] = clonedObject[key]; } };
      
      





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:





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?





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(); // starting from 1995 and then ECMA 262 // we are able to say new // each time we need a ChildObject
      
      





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:



 // this Assignment is just to show it grew up var ChildObjectGrownToParent = ChildObject; var AnotherConstructor = function () {}; AnotherConstructor.prototype = ChildObjectGrownToParent; var SequentialChildObject = new AnotherConstructor(); // checking Life Cycle ;^) console.log(ChildObject instanceof ParentConstructor); // true console.log(SequentialChildObject instanceof ParentConstructor); // true console.log(SequentialChildObject instanceof AnotherConstructor); // true
      
      





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' }; // factory of constructors var FlowersFactory = function (proto) { var FlowerEggCell = function (sort) { this.sort = sort; }; FlowerEggCell.prototype = proto; return FlowerEggCell; }; var FlowerZygote = FlowersFactory(Pollen); var galanthus = new FlowerZygote('Galanthus');
      
      





(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



All Articles