📜 ⬆️ ⬇️

A revolution in javascript. Literally

Today is April 22, the anniversary of 2017, the birthday of a person, without which the events would not have happened a hundred years ago. So, there is a reason to talk about revolutionary history. But where is Habr? - It turned out that everything in this world can have the most unexpected connection.

Vladimir Ilyich Lenin

As we know, JavaScript can be considered as an object-oriented language in the sense that “it all comes down to objects” (in fact, to the prototype of an object). On the other hand, in the broad philosophical question, we also now and then deal with objects and prototypes. For example, when considering the object of the Revolution and its prototype, described in the "Capital". So let's just talk about those events in modern language!

With all the revolutionary directness, just trumps on the table!
This article is essentially a brief explanation of the mechanism of inheritance in JavaScript, that is, one of the parties to the PLO. The advanced proletarians-developers will find absolutely nothing new in it. However, I hope the material can serve as a memorable, figurative reminder for a wide range of people interested in developing JS. In addition, it is possible that someone will tighten their knowledge of national history.

Create two objects:
')
var stalin = { gulag: true, mustache: true, hero: 1 } var lenin = { baldHead: true, armand: false, criticalImperialism: true } 

We point out that one object is the heir of another through the __proto__ property (this form of recording is available in all browsers except IE10-, and is included in ES2015 ). One heir, and the other - a prototype. We check the properties of the heir object, __proto__ appeared there:

 stalin.__proto__ = lenin; console.log(stalin); 

If the property is not detected directly in the object, it is searched in the parent object (prototype). Let's try:

 console.log(stalin.baldHead); // true 

Yes, the property is available, but its value does not suit us. Overwrite it, but the property of the parent object does not change:

 stalin.baldHead = false; console.log(lenin.baldHead); // true -      

By the way, what is the prototype of the prototype?

In JS, an object, except for one case (about this below), inherits from Object .__ proto__ (look in the console). This includes standard methods that are available by default: for example, Object.toString (), Object.valueOf (), and so on.

And how do we enumerate the properties of the object itself, without the properties of its parent, in order not to perform unnecessary operations? - For this, there is hasOwnProperty:

 for (var key in stalin) { if (stalin.hasOwnProperty(key)) console.log(key + ": " + stalin[key]) } 

By the way, if the object already has its own property, after assigning the prototype, it will not be overwritten by the value from the prototype, but will remain as it was:

 var dzerjinskiy = { mustache: true, baldHead: false } dzerjinskiy.__proto__ = lenin; console.log(dzerjinskiy.baldHead); // false -       

Finally, you may need a simple dummy object with no properties, which is only needed for writing values. Then we should not have to check hasOwnProperty when listing its properties:

 var zyuganov = Object.create(null); zyuganov.experience = 25; console.log(zyuganov.toString); // undefined 

When checking, it turns out that the empty object does not even have standard methods, such as toString (). By the way, the Object.create method (prototype [, {}]) was used above. It is a method that allows you to create an object with a mandatory indication of the prototype (including null) and properties (optional).

F.prototype and new

You can create a constructor to make it easier to create instances whose parent is a single object:

 var marks = { marxism: true, engels: "friend", beard: 80 } function Marksist(name) { this._name = name; this.__proto__ = marks; } var kamenev = new Marksist("kamenev"); console.log(kamenev); 

We see that Comrade. Kamenev also has a beard

Lev B. Kamenev

However, what about the Revolution? You can add a new value and method to the prototype, then a child can use this method:

 marks.revolution = "future"; marks.deal = function() {return this.revolution}; // this    marks 

New value appeared in the descendant:

 console.log(kamenev.revolution); // "future" 

We add a property or method to the prototype, and it appears in descendants without the need to redefine them. The power of prototype inheritance!

Naturally, the value in the descendant can be modified, the remaining descendants of the prototype will not be affected:

 kamenev.revolution = "now"; console.log(kamenev.deal()); // "now" 

As you can see, the object did not initially have a method, however, after adding the method to the prototype, we can call it, moreover, with the values ​​modified in the child.

For support in all browsers, incl. old, there is another way:

 function Marksist(name) { this._name = name; } Marksist.prototype = marks; var kamenev = new Marksist(""); console.log(kamenev); //         marks  __proto__ 

Prototype makes sense only in constructors (written in capital letters to JS), it essentially performs only one action, namely: it indicates where to refer to the __proto__ property when initializing the constructor function.

If after the creation of the first object we want to create a second one, pointing to it a different prototype, the prototype of the first object will not change:

 Marksist.prototype = {}; var chicherin = new Marksist(""); console.log(chicherin.marxism); // undefined,   __proto__    Object console.log(kamenev.marxism); // - true,   __proto__   marks 

It is seen that the new object with an empty prototype does not have inherited properties, like the first object. But everything can be replayed on the fly:

 Marksist.prototype = marks; var zinovev = new Marksist(""); console.log(zinovev.marksizm); // true console.log(zinovev.deal()); // future 

It should be noted that changing prototypes is considered to be a very expensive operation, so playing with prototypes on the fly is not recommended!

In the prototype, we can also define methods that all descendants will use:

 var marks = { marxism: true, engels: "friend", beard: 80, shout: function(){ alert("   " + this._name + "!") } } function Marksist(name) { this._name = name; } Marksist.prototype = marks; var dzerjinskiy = new Marksist(""); dzerjinskiy.shout(); //    ! 

Here this is the object whose function is called from the prototype, in this case Dzerzhinsky.

Felix Edmundovich Dzerzhinsky
Correctly Felix Edmundovich warns us: in JavaScript you should always be vigilant about where the keyword this is currently pointing.

You can check whether the object is a constructor heir by using the instanceof operator:

 var zemlyachka = function(tov) { var res = false; if (tov instanceof Marksist) res = true; return res; } console.log(zemlyachka(zinovev)); // true 

We cite an opportunist who will have in practice all the same properties and methods as an ordinary Marxist:

 var opportunist = { name: "", marxism: true, engels: "friend", beard: 80, shout: function(){ alert("   " + this.name + "!") } }; opportunist.shout(); 

We can even tell him the same unique property, and determine the rest in his prototype, that is, keep exactly the same structure as the previous objects:

 var plehanov = { marxism: true, engels: "friend", beard: 80, shout: function(){ alert("   " + this._name + "!") } } function Socialist (name){ this._name = name; } Socialist.prototype = plehanov; var opportunist = new Socialist(""); console.log(opportunist); //        var zinovev = new Marksist,     

However, on checking it will be filled up:

 console.log(zemlyachka(opportunist)); // false 

Rosalia Samoylovna Zemlyachka
Rosalia Samoilovna sees the object through. There is another approach to checking objects -
Duck typing
If it looks like a duck, swims like a duck and quacks like a duck, then it may be the duck.
If the subject object behaves as we need, then we consider it a communist who we should consider, despite its origin


However, proven communists can sometimes be mistaken. The instanceof operator compares only the prototypes of the object and the constructor, so collisions like this are possible:

 var troczkiy = new Marksist(""); Marksist.prototype = {}; console.log(troczkiy instanceof Marksist); // , 1940-   ! 

And of course, we remember that everything in JS is an object (or rather, in the prototype chain, all objects, except special empty ones, come to Object.prototype ), so the test will return true both times:

 var john_reed = [10]; console.log(john_reed instanceof Array); // true console.log(john_reed instanceof Object); // true 

Constructor property

Constructor functions (and indeed all functions) have a prototype property in which the constructor is written: it returns a reference to the function that created the instance prototype. It can be easily lost in further transformations, since JS is not required to maintain this link. Let's say we decided to find out the political roots of Lev Davydovich:

 var marks = { marxism: true, engels: "friend", beard: 80 } function Marksist(name) { this._name = name; } Marksist.prototype = marks; var troczkiy = new Marksist(""); var Congress = troczkiy.constructor; var retrospective = new Congress("My life"); console.log(retrospective); //  ,     ,     ! 

Lev Davidovich Trotsky

Obviously, we could not call the same constructor function that would create a new object identical to the first one (although the constructor , in theory, should have pointed to it!). To get the desired result, simply save the constructor property in the Marksist prototype:

 var marks = { marxism: true, engels: "friend", beard: 80 } function Marksist(name) { this._name = name; } Marksist.prototype = marks; Marksist.prototype.constructor = Marksist; var troczkiy = new Marksist(""); var Congress = troczkiy.constructor; var retrospective = new Congress("My life"); console.log(retrospective); //     ! 

Thus, it is not necessary for us to know how the designer created the instance, from which we now create a new instance. This information is recorded in the copy.

Looking at these transformations, the thought may come to override the properties and methods of embedded JavaScript prototypes. Speaking in a revolutionary language, move to a global, earthy level

The first emblem of the USSR 1924

Nothing will stop us from, for example, changing the Object.prototype method:

 Object.prototype.toString = function(){ alert(" !") } 

Or even a non-extremist example:

 Array.prototype.manifest= function(){ return "    -  "; } 

This style of class change (it would be more accurate to say prototypes here) is called monkey patching.

There are two dangers. First, by expanding or modifying the built-in prototypes, we make changes available to all objects lying down the property inheritance chain (for the Object.prototype, these are all JS entities). Then, using such a method, we risk calling the new property the old name, thereby wiping it. If within the same prototype chain we can remember the names of properties in a large project, where, besides, other scripts can be connected, the contents of the prototypes of basic entities, like the global object, are better not to touch, otherwise the consequences will be unpredictable.

And secondly, redefinition in the course of program execution can lead to unobvious results:

 var pss = { name: "  ", author: "", length: 20 } var Books = function(){}; Books.prototype = pss; var firstEdition = new Books; console.log(firstEdition.length); // 20 var extendLenin = function(year){ if (!year) var year = new Date().getFullYear(); if (year > 1925 && year < 1932) pss.length = 30; if (year > 1941 && year < 1966) pss.length = 35; if (year > 1966) pss.length = 55; } extendLenin(); var fourthEdition = new Books; console.log(fourthEdition.length); // ?? 

The behavior of the properties of objects inheriting from the prototype (the so-called "class instances") may not be the same as we assume.

Functional inheritance

In JavaScript, besides the prototype inheritance paradigm, functional is also used. On the example of one of the heroes of the Revolution, let's see how it is implemented.

Create a constructor that:

a) Accepts parameters
b) It has public methods that are supposed to be used outside the constructor and its derivatives.
c) Has private methods that are supposed to be used only inside the constructor and its derivatives.

Before us is a typical communist:

 var Kommunist = function(principles) { if (!principles) principles = {}; this._start = 1902; if (principles && principles.start) this._start = principles.start; //  ,     this.response = function() { alert("  !") } //  ,         this._experience = function() { return (this._start); } this.principles = (Object.keys(principles).length > 0 ? principles : {fidelity: 100}); this._getPrinciples = function() { return this.principles } } 

Private methods are usually written starting from the underscore.

So, we have a constructor with a set of methods, ready to accept and process arguments. Create an instance of the class:

Kliment Yefremovich Voroshilov

 function Voroshilov(principles) { Kommunist.apply(this, arguments); //    var parentExperience = this._experience; this._experience = function() { return ("  ()  " + parentExperience.apply(this, arguments)); } //  ,     //  this.getPrinciples = function() { var p = this._getPrinciples(); var char = { fidelity: p.fidelity, persistence: p.persistence || "!" } console.log(": " + char.fidelity + ", : " + char.persistence) } this.getExperience = function() { console.log(this._experience()); alert(" -!"); } //  this.setExperience = function() { this._experience = function() { return ("  ()   "); } } } var ke = {fidelity: 101, persistence: 100, start: 1903} var voroshilov = new Voroshilov(ke); 

Note: the constructor is called relative to this to write all its methods into it, and with the arguments array, which contains all the arguments specified during the call ( ke object).

Then we can observe how the getter and setter work, as well as other public methods:

 voroshilov.getExperience(); //   voroshilov.setExperience(); //       voroshilov.getExperience(); //    voroshilov.getPrinciples(); //         

For variety, you can call the constructor without parameters.

Class entity

Finally, with the release of ES6 (ES2015), we have the opportunity to use the class instruction directly in JavaScript. In fact, nothing has changed in the prototype inheritance device, but now JS supports syntactic sugar, which will be more familiar to many programmers who come from other languages.

 class Marksist { constructor(name) { this._name = name } enemy() { return "capitalism" } static revolution(){ return " " } } 

JS classes have three kinds of methods:

- constructor (performed when initializing an instance of a class);
- static (static methods available when calling a class, but not available in instances);
- conventional methods.

Now let us remember in the constant (ES6 admits this type of variables) a very important date, and then we define a Menshevik who is a Marxist heir:

 const cleavage = 1903; class Menshevik extends Marksist { constructor(name) { super(); this._name = name; } static revolution() { var r = super.revolution(); return r + ",  "; } ["che" + cleavage]() { alert(" !") } hurt() { alert("  ") } } let chkheidze = new Menshevik(""); 

image

There are two innovations here:

- super () in the first case initializes the constructor of the base class, in the second it calls the method to which we add new behavior to the child;
- calculated method names ( [“che” + cleavage] ), now we don’t need to immediately know the name of the method.

The static method is available when the class is called, but not when the instance is called:

 console.log(Menshevik.revolution()); //  console.log(chkheidze.revolution()); // is not a function,     

The result of the following code is already clear:

 chkheidze.hurt(); //    console.log(chkheidze.enemy()); //     chkheidze.che1903(); //      

Above, the most basic features of inheritance through classes ( class ) in JavaScript have been shown. Conscious proletarian with proper revolutionary perseverance will find in the network a lot of articles, more fully covering the issue of innovations in ES6.

Source: https://habr.com/ru/post/324640/


All Articles