Hello, Habr!
To this day, I have only been engaged in translations of articles, interesting, in my opinion, by English-speaking authors. And now it's time to write something yourself. For the first article, I chose a topic that, I am sure, will be useful to junior developers who want to grow to the "middle", because it will analyze the similarity / difference between JavaScript and classical programming languages (C ++, C #, Java) in terms of OOP. So, let's begin!
General Paradigm Provisions
If we look at the definition of JavaScript on
Wikipedia , we will see the following concept:
JavaScript (/ ˈdʒɑːvɑːˌskrɪpt /; abbr. JS /ˈdʒeɪ.ɛs./) is a multi-paradigm programming language. Supports object-oriented, imperative and functional styles. It is an implementation of the ECMAScript language (ECMA-262 standard).
As follows from this definition, JavaScript does not exist by itself, but is an implementation of some EcmaScript specification. In addition to it, other languages implement this specification.
The following paradigms are present in EcmaScript (hereinafter ES):
- structural
- OOP
- functional
- imperative
- aspect-oriented (in rare cases)
OOP in ES is implemented at a
prototype organization . From novice developers in response to the question: "How is OOP in JS different from OOP in classical languages." As a rule, they get very vague: “Classes are in classical languages, and prototypes in JS”.
In reality, the situation is a bit more complicated. In terms of behavior, the difference between the
Dynamic Class Organization and the
Prototype Organization is small (it certainly exists, but not so global).
Take a look at Python or Ruby. In these languages, OOP is based on a dynamic class organization. In both of these languages, we can dynamically change the class of an object as the program progresses and changes within the class also dynamically affect the entities it generates. Just like in JS, but in JS, OOP is based on prototypes.
A significant difference between languages with a Static class organization and a Prototype organization. The difference in itself is “there are classes. here prototypes ”are not so significant.
What is Static Class Organization based on?
The basis of this type of OOP are the concepts of “Class” and “Essence”.
A class is a certain formalized generalized set of characteristics of entities that it can generate. Those. This is a certain general plan of all objects generated by it.
Characteristics are of two types. Properties (description of an entity) and methods (entity activities, their behavior).
Entities generated by a class are copies of this class, but with initialized properties. As we see, the class strictly regulates the description of an entity (providing a strictly defined set of properties) and its behavior (providing a strictly defined list of methods).
Here is a small example on JAVA:
class Person{ String name;
Now create an instance of the class:
public class Program{ public static void main(String[] args) { Person tom; } }
Our
tom entity has all the characteristics of the
Person class, it also has all the methods of its class.
The OOP paradigm provides a very wide palette of features for reusing code, one of these features is
Inheritance .
One class can extend another class, thereby creating a generalization-specialization relationship. In this case, the properties of the general class (superclass) are copied to the essence of the descendant class when they are created, and the methods are available by reference (according to the hierarchical chain of inheritance). In the case of static class typing, this chain is
static , and in the case of dynamic typing, it can change during program execution. This is the most important difference. I advise you now to remember this moment. Further, when we get to the Prototype organization, the essence of the problem of the answer “there are classes, there are prototypes” will become obvious.
What are the disadvantages of this approach?
I think it’s obvious that:
- In essence, there may be characteristics that will never come in handy.
- A class cannot dynamically change, add, delete properties and methods that it provides to generated entities, i.e. cannot change his signature.
- In essence, properties or methods that are not present in the class of the parent (or hierarchical chain of parents) cannot be present
- Memory consumption is proportional to the number of links in the inheritance hierarchy (due to copying properties)
What is the prototype organization based on?
A key concept of the prototype organization is a dynamic mutable object (dmo). DMO does not need a class. He himself can store all his properties and methods.
When setting a DMO of a property, it checks for the presence of this property in it. If there is a property, then it is simply assigned; if it is not, then the property is added and initialized with the passed value. DMOs can change their signature during the program as many times as they like.
Here is an example:
I think everyone in the subject knows that class syntax has appeared in ES6, but this is nothing more than syntactic sugar, i.e. prototypes under the hood. The code above should not be taken as good coding practice. This is nothing more than an illustration, it is presented in this form (now all normal people use ES6 classes) so as not to confuse the reader and emphasize the difference in theoretical concepts.
If we output the Tom object to the console, we will see that the object itself has only the _proto_ link, which is always present in it by default. The reference points to the Person object, which is a prototype of the Tom object.
A prototype is an object that serves as a prototype for other objects or an object in which another object can draw properties and methods if they are needed.
The prototype for an object can be any object, moreover, an object can reassign its prototype during the program.
Let's get back to our Tom:
Tom.name = 'Tom';
Note that the name, age properties and sayHi method are the properties of the tomSon object. At the same time, we in tomSon sayHi explicitly call the sayHi prototype method as if it were in the Tom object, but in fact it is not there, and it returns implicitly from the Person prototype. We also explicitly operate on the prototype property name and implicitly get the surname property, which we call as our own property of the tomSon object, but actually it is not there. The surname property is implicitly pulled through the
__proto__ link from the prototype.
We continue the development of the history of our Tom and his son John.
Please note that during the program we changed the prototype of the already created object. This is the similarity of the
Prototype Organization and the
Dynamic Class Organization . That is why the answer "there are classes, there are prototypes" to the question "what is the difference between classical languages and JavaScript?" not quite correct and indicates some misunderstanding of the theory of OOP and its implementation on classes and / or prototypes.
With the Prototype organization, unlike the Static class one, we have the opportunity to make changes to the prototype after creating an entity that inherits the properties from this prototype, and these changes will affect the already created entity.
Ben.hobbies = ['chess', 'badminton'];
This is called
the prototype organization delegating model or
prototype inheritance .
How is the ability of an entity to implement certain behavior determined?
In a static class organization, this operation involves checking the entity for membership in a particular class that implements the required behavior. In the prototype organization, there is the concept of
duck typing . In the case of duck typing, checking the entity for the ability to implement a specific behavior will mean directly testing the entity for the ability to implement this behavior at a particular point in time, i.e. in different parts of the program, the result of the check may be diametrically opposite.
What are the advantages of a prototype approach?
- More flexibility
- Entities do not have properties that they do not need.
What are the downsides?
- Less clear
- It is not always easy to track what served as the starting point for the entity’s unwanted behavior, i.e. prototype is less predictable than static class organization
- The software development community is not familiar enough with it, despite the popularity and prevalence of JavaScript
Conclusion
On this we will end today. I hope that I was able to convey the idea that the difference between classical languages and JavaScript is not related to the presence / absence of classes and the presence / absence of prototypes, but rather to the static / dynamic nature of the organization.
Of course, much has not been considered. I would not want to write articles that are too long, so we will discuss the features of the Cascade Model in the prototype organization and OOP tools (Polymorphism, Encapsulation, Abstraction, etc.) in subsequent articles.