This article is available in English with a short video presentation on the site Today Software Magazine
The best advice for applying inheritance is to use it as described in EcmaScript 6, with the keywords class, extends, constructor, etc. If you have such an opportunity,Consider some popular examples of the implementation of classical inheritance.you can not read further,then this is the best option in terms of code readability and performance. All the following description will be useful for the case of using the old specification, when the project has already been started using ES5 and the transition to the new version is not available.
instanceof
operator, etc. class BasicClass { static staticMethod() {} constructor(x) { this.x = x; } someMethod() {} } class DerivedClass extends BasicClass { static staticMethod() {} constructor(x) { super(x); } someMethod() { super.someMethod(); } }
_inherits
function: function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
subClass.prototype = Object.create(superClass.prototype);
prototype
property of the subClass
constructor points to a new object, the prototype of which is the prototype
parent class superclass
. Thus, this is a simple prototypal inheritance disguised as classical in the source code. Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
setPrototypeOf
function setPrototypeOf
Babel provides for direct recording of the prototype into the hidden __proto__
property - a technique that is not recommended, but suitable for extreme cases when using old browsers._inherits
call _inherits
simply copying the references into the constructor or its prototype
. When writing your own implementation of inheritance, you can use this example as a basis and add objects with dynamic and static fields to it as additional arguments to the _inherits
function. return _possibleConstructorReturn(this, (DerivedClass.__proto__ || Object.getPrototypeOf(DerivedClass)).call(this, x));
this
._super
fields to the constructor and its prototype
to have a convenient reference to the parent class, for example: function extend(subClass, superClass) { // ... subClass._super = superClass; subClass.prototype._super = superClass.prototype; }
extend
function for extending library classes: Model, View, Collection, etc. If you wish, you can borrow it for your own purposes. Below is the extend
function code from Backbone 1.3.3. var extend = function(protoProps, staticProps) { var parent = this; var child; // The constructor function for the new subclass is either defined by you // (the "constructor" property in your `extend` definition), or defaulted // by us to simply call the parent constructor. if (protoProps && _.has(protoProps, 'constructor')) { child = protoProps.constructor; } else { child = function(){ return parent.apply(this, arguments); }; } // Add static properties to the constructor function, if supplied. _.extend(child, parent, staticProps); // Set the prototype chain to inherit from `parent`, without calling // `parent`'s constructor function and add the prototype properties. child.prototype = _.create(parent.prototype, protoProps); child.prototype.constructor = child; // Set a convenience property in case the parent's prototype is needed // later. child.__super__ = parent.prototype; return child; };
var MyModel = Backbone.Model.extend({ constructor: function() { // ; , // , Backbone.Model.apply(this, arguments); }, toJSON: function() { // , «__super__» MyModel.__super__.toJSON.apply(this, arguments); } }, { staticMethod: function() {} });
child.prototype = _.create(parent.prototype, protoProps);
_.create()
function is an analogue of Object.create()
from ES6, implemented by the Underscore JS library. Its second argument allows you to immediately write to the prototype the properties and methods of protoProps
passed when you call the extend
function. _.extend(child, parent, staticProps);
initialize
method, which is called automatically from within the parent constructor.this
. Without this, such a call would lead to looping in the case of a multilevel chain of inheritance. The superclass method, whose name is usually known in the current context, can also be invoked directly, so this keyword is only an abbreviation for: Backbone.Model.prototype.toJSON.apply(this, arguments);
extend
function - “child”. This drawback may seem insignificant until you come across it in practice when debugging a chain of classes, when it becomes difficult to understand which class is the object and which class it inherits from:constructor
property is enumerable, i.e. enumerated when traversing an instance of a class in a for-in loop. Not significant, however, Babel took care of this, declaring the constructor with the enumeration of the necessary modifiers.inherits
function implemented by Babel and its own extend
implementation, which is very complex and sophisticated, with support for mixins and so on. There is simply not enough room to bring the code of this function in this article, which already casts doubt on its performance when used for its own needs outside the framework. var MyClass = MySuperClass.extend({ myMethod: function (x) { this._super(x); } });
this._super(x)
) we do not specify the name of the method. And no code transformations occur during compilation._super
which method to call when accessing the _super
universal property without code conversion? It is all about the complex work with classes and the clever function _wrap
, the code of which is given below: function _wrap(func, superFunc) { function superWrapper() { var orig = this._super; this._super = superFunc; // <--- var ret = func.apply(this, arguments); this._super = orig; return ret; } // return superWrapper; }
superWrapper
._super
property, a _super
written to the parent method corresponding to the name of the method being called (the work on determining correspondences occurred at the stage of creating a class when invoking extend
). Next, the original function is called, from inside of which you can access _super
as the parent method. Then the _super
property _super
assigned the original value, which allows its use in deep call chains._super
property is _super
in it, is wrapped in a separate function. Therefore, with a deep chain of calls to methods of the same class, a stack will grow. This is especially critical for methods that are called regularly in a loop or when drawing the user interface. Therefore, it can be said that this implementation is too cumbersome and does not justify the advantage obtained in the form of an abbreviated form of recording. function BaseClass() { this.x = this.initializeX(); this.runSomeBulkyCode(); } // ... BasicClass ... function SubClass() { BaseClass.apply(this, arguments); this.y = this.initializeY(); } // SubClass.prototype = new BaseClass(); SubClass.prototype.constructor = SubClass; // ... SubClass ... new SubClass(); //
prototype
, an instance of the parent class is created, its constructor is called, which leads to unnecessary actions, especially if the constructor does a lot of work when creating the object ( runSomeBulkyCode ). So you can not do: SubClass.prototype = new BaseClass();
this.x
) are written not to the new instance, but to the prototype of all instances of the class SubClass . In addition, the same BaseClass constructor is then called again from the subclass constructor. If the parent constructor requires some parameters to be called, it is hard to make such an error, but if they are not available, it is quite possible. SubClass.prototype = Object.create(BasicClass.prototype);
Babel | Backbone js | Ember js | |
---|---|---|---|
Memory | Equivalently | ||
Performance | Higher | Average | Lower |
Static fields | + (only in ES6) * | + | - (except for the internal use of inheritance from Babel) |
Superclass reference | super.methodName() (only in ES6) | Constructor.__super__.prototype | this._super() |
Cosmetic items | Ideal with ES6; needs some work in its own implementation under ES5 | Convenience ads; debugging issues | Depends on the mode of inheritance; same debugging problems as in backbone |
extend
function based on an example from the Babel compiler, taking into account the comments and additions from other examples. So inheritance can be implemented in the most flexible and appropriate way for this project.Source: https://habr.com/ru/post/353778/
All Articles