
Translation NotesI raise the continuation of the abandoned translation, since the questions in the original are closely intertwined with inheritance questions made in our own compact library for use without frameworks that have OOP support. Let both not be original, but together gives an understanding of the work of inheritance.
For completeness of the article and a single style, the translation begins with inheritance questions, despite the fact that they were already mentioned at the end of the first part . Further, various problems of inheritance are considered as they were considered by the author. It should be noted that the author makes wide use of new constructions of ES5 (explaining this at the end), which do not work in all browsers and obscure their implementation at a low level of the language in which they were originally used. For a real understanding of inheritance, you should refer to a deeper analysis of implementations or to the implementations of the wrapper methods from ES5: Object.create, Object.defineProperty, Function.bind, get and set literals, Object.getOwnPropertyNames, Object.defineProperty, Object.getOwnPropertyDescriptor, Object .getPrototypeOf. Some of them understand the article (Object.create, get and set, Object.defineProperty, bind), but not always in the order of appearance. Thus, the article seeks to present not the implementation of inheritance in general, but the implementation that was formalized in the working draft of the EcmaScript 5 standard. This is better than nothing, but somewhat less than a complete understanding of the implementations of inheritance.
')
On the other hand, this part of the article in several (4) large code examples demonstrates the purest prototypical inheritance, which does not need to involve the notion of a constructor (although it is there, invisibly present in .create ()), which is much talked about and which is extremely rare in its pure form is found. Summary of the first part1. Objects1.1 What are objects? (list of properties)
1.2 Creating properties (Object.defineProperty)
1.3 Property descriptors (Object.defineProperty)
1.4 Syntax parsing (bracket notation: object ['property'])
1.5 Access to properties (through the bracket notation)
1.6 Deletion of properties (operator delete)
1.7 Getters and Setters (Access and Record Methods)
1.8 Property Lists (getOwnPropertyNames, keys)
1.9 Literals (base operators) object
2. Methods2.1 Dynamic this
2.2 How this is implemented
2.2.1 If called as an object method
2.2.2 With a normal function call (this === global)
2.2.3 When explicitly specifying the context (.apply, .call)
2.3 Binding methods to context (.bind)
Part 3 plan4. Designers
4.1 Magic of the operator new
4.2 Inheritance with constructors
5. Conventions and compatibility
5.1 Creating Objects
5.2 Definition of properties
5.3 Property Lists
5.4 Methods of binding
5.5 Getting [ [Prototype] ]
5.6 Backward Compatibility Libraries
6. Syntactic wrappers
7. What to read next
8. Acknowledgments
Notes
3. Prototype Inheritance
So far, we have considered how methods are defined in objects and how they are reused in other objects when the context is explicitly indicated, but this is still not the best way to use and extend objects.
Next comes inheritance. It separates concepts better when objects are endowed with their own methods based on the methods of other objects.
Prototype inheritance goes further and can selectively extend methods, describe general behavior and use other entertaining techniques that we touch. The only sadness is that the model of inheritance in JS is a bit limited, and to circumvent the difficulties, these techniques will at times be
excessive to carry the brain.
3.1. Prototypes
The idea of inheritance in Java script revolves around cloning the methods of an object and supplementing it with its own behavior. An object that is cloned is called a prototype (not to be confused with the prototype property of functions).
A prototype is a regular object that has had the opportunity to extend the methods of another object - it acts as the parent of the object.
(This is a somewhat biased concept with respect to the generally accepted one, when the parent calls a constructor function containing this prototype. Whenever possible, the translation tries not to use the concept of a parent as applied to the prototype - comment.)However, cloning does not mean that you will have different copies of the functions or data. In fact, JS implements inheritance through delegation: all properties are stored by the parent, and heirs are allowed to be used.
Our example so far fits well with this model. For example, name and greeting methods can be described in a separate object and shown where appropriate. Which leads us to the following model:
It is described in JS with the following code:
(hardcore new syntax, for ES5. Recall that the arguments in defineProperty are the object, its name and the special object being assigned - note.)continuation of the script from the examples to the 1st part of the article jsfiddle (1) for non-believers (IE9 +)3.2. How does [[Prototype]] work
As you can see from the example, not a single property from a person has been mentioned in mikhail, but they all work fine, because access to properties is transferred (delegated) to JS, i.e. properties are searched in all parents of the object.
The parent chain is defined in the hidden objects of each parent, called [[Prototype]]. They cannot be changed directly (except for implementations where .__ proto__ is supported), so the only (specified) method is setters when created.
When a property is requested from an object, the interpreter checks its own properties for the object. If there is no such property, the parent is checked, and so on - to the end of the parent chain or to the first existing property.
If we change the property of a prototype, it will immediately change for all prototypes of other heir objects.
3.3. Property Overriding
Thus, prototypes and inheritance are used to separate data access for different objects and are performed very quickly and efficiently in memory costs, since a single data source is used for all heirs.
What if you need to add specialized methods based on the data that existed at the time of inheritance? We have seen before that methods are determined based on properties, so we will define specific behavior in the same way — just assign new properties.
To demonstrate, suppose that Person implements a general greeting, and successors of Person define their own. In addition, add another person to see the difference.
Note that mikhail and kristin have individual greetings expressed by versions of the
greet method.
... example in codes(We will not further fully describe the definitions of the variables used in the code examples - they are in duplicate examples on jsfiddle.net (above the dividing line from "=====") or are easily added on the basis of previous examples from the article - note. )
jsfiddle (2)3.4 Mixins (impurities)
Javascript prototypes allow the use of common methods, and although they are undoubtedly a powerful tool, they could be even more powerful. They only provide for the inheritance of one object by another at the time of inheritance.
But this approach does not implement other interesting cases when it is necessary to do a composition of methods, mixing and combining several objects in one with all the advantages of prototype inheritance.
For example, multiple inheritance would allow the use of objects - data sources, giving the default settings of methods or properties.
Fortunately, since we directly define the methods of objects, we can solve these problems with impurities — some additional definition of objects during their creation.
What is impurity? It can be said that they are “rootless” objects that are not inherited from anywhere. They are fully defined in their properties-methods and, most often, made for inclusion in other objects (although their methods could be used directly).
As we develop our small character model, let's add some abilities to them. Let a person be a pianist or a singer - to have pianist or singer methods in arbitrary combinations. This case does not fit into the prototype model, so let's go for a little trick.
(Actually, it is possible to replace mixins with a variable chain of inheritance with prototypes, therefore the choice of mixin is a matter of convenience and optimal implementation, and not a consequence of impracticability in the model of prototypes. - comment.)For the work of mixins, first of all, we combine different objects into one. JS does not natively support this unusual object format, but it is easily created by copying all
its own (non-inherited) properties.
var descriptor = Object.getOwnPropertyDescriptor
extend () here enumerates its own source properties and copies them to target. Note that the target will change, for it this function is destructive, which is usually not a problem. More importantly, it is the least expensive.
Now we can add “abilities” to our objects.
Add to the codes an admixture of abilities var pianist = Object.create(null);
jsfiddle (3) for easy monitoring (IE9 +)3.5. Access to shielded properties
We learned to inherit properties and expand them with mixins. Now there is a small problem: what to do if we want to get access to the overwritten (screened) property of the parent object?
JS provides the Object.getPrototypeOf function which returns [[Prototype]]. Therefore, access to the properties of the prototype is quite simple:
Object.getPrototypeOf(mikhail).name;
It would be reasonable to assume that reference to the prototype context (this) is sufficient:
var proto = Object.getPrototypeOf;
It looks good, but there is a hitch: if you try to apply an approach not to the immediate ancestor, endless recursion will occur because
this always sees the closest context of the function and will fall on the same parent object, as illustrated:
A simple solution is to take a prototype from the parent object, not from the current one. The last example becomes

:
var proto = Object.getPrototypeOf;
The method is not without flaws: the object is rigidly specified in the function, and we cannot simply take and apply the function to any object, as it has been until now. The function will be dependent on the ancestor of the object, and not on itself.
If you make a dynamic, universal access to the prototype of the parent, it would require the transfer of an additional parameter for each function call, which cannot be resolved, as it is now, quickly in ugly hacks.
(Namely, each inheritance must be accompanied by a property of type .ancestor or .superclass to access the ancestor constructor, and functions - to use this data - comment.)The approach proposed in the new version of JS solves only the first part of the problem, which is the simplest. Here we will do the same, but by introducing another way of defining methods. Yes, methods, not common functions.
Functions to access properties in [ [Prototype] ] require additional information: the object where they are recorded. This requires a search algorithm that works with static data, but solves our recursive problems.
We introduce the function make_method, which returns the function that passes this information to the target function.
(Ie, you need to get a reference to the prototype, in which our screened method is declared, and this is achieved by modifying the method at each step of inheritance - note of the translation.)three-step inheritance to demonstrate access to shielded properties
(For better clarity, this example has been greatly changed in the output format of the trace compared to the original article; corrected the original misprints - note of the translation.) jsfiddle (4), for lovers of scratchTo be continued.