📜 ⬆️ ⬇️

OOP in javascript

I want to introduce you to the class constructor function createClass.
What good is this constructor:
1. The code itself looks more readable and understandable.
2. Support multiple inheritance
3. Support abstract methods
4. Inheritance of static methods and class properties
5. Intelligent instanceOf method (checks the entire class chain)

So, immediately an example:
var Animal = createClass({ abstracts: ['say'], construct: function(name) { this.name = name; }, eat: function() { return 'ou yeah...'; } }); var Kitty = createClass({ extend: Animal, say: function() { return this.name + ' said: RrrrRRR...Mau..'; }, eat: function() { return Animal.fn.eat.call(this) + 'FISH!!' } }); var Doggy = createClass({ extend: Animal, construct: function(name) { Animal.call(this, name); console.log('doggy have been created'); }, say: function() { return this.name + ' said: GAV!'; }, eat: function() { return Animal.fn.eat.call(this) + 'MEAT!!' } }); var sharik = new Doggy('sharik'); // doggy have been created var murka = new Kitty('murka'); console.log( sharik.eat(), sharik.say()); // ou yeah...MEAT!! sharik said: GAV! console.log( murka.eat(), murka.say()); // ou yeah...FISH!! murka said: RrrrRRR...Mau.. 

All properties of the class must be defined in the constructor (static properties in the static constructor, it will be discussed below).
The Animal class has an abstract “say” method. The Kitty and Doggy classes inherit from Animal and implement the “say” method. In this case, since in Kitty the constructor is not explicitly specified, it is borrowed from Animal. In Doggy, the constructor is given explicitly, so the parent constructor must also be called explicitly Animal.call (this, name). The parent class's eat method can be called as Animal.fn.eat.call (this) is the same as Animal.prototype.eat.call (this)

Farther:
 var Singleton = createClass({ statics: { construct: function() { this._instance = null; }, getInstance: function() { if(this._instance === null) { this._instance = new this(); } return this._instance; } } }); var SuperDoggy = createClass({ extend: [Animal, Singleton], say: function() { return this.name + ' said: GAV! GAV! GAV!'; } }); var dog1 = SuperDoggy.getInstance(), dog2 = SuperDoggy.getInstance(); dog1.name = 'Bob'; console.log(dog1 === dog2); // true console.log(dog1.eat(), dog1.say()); // ou yeah... Bob said: GAV! GAV! GAV! 

Singleton has a static _instance property and a static getInstance method. This class can be used as an implementation of the Singleton pattern. SuperDoggy is inherited from the two classes Animal, Singleton and implements the “say” method.
It is worth noting the construct method in the statics object. This method is called when creating the class itself and when creating a subclass. It allows you to correctly copy the static properties of an object when inheriting. If the prototype object has a constructor method that creates an object, then why not have a constructor that creates the class itself?

Examples of using the “instanceOf” method:
 console.log( dog2.instanceOf(Kitty) === false ); // true console.log( dog2.instanceOf(SuperDoggy) === true ); // true console.log( dog2.instanceOf(Animal) === true ); // true console.log( sharik.instanceOf(Animal, Singleton) === false ); // true console.log( dog2.instanceOf(Animal, Singleton) === true ); // true 

Initially, the idea was taken from the book of David Flenagan JavaScript 5 Edition: an example from the book . As a result, this code has evolved into the one presented in the article.
')
Since the source is not so great, publish it right here:
 function createClass(data) { var abstracts = data.abstracts || [], statics = data.statics || {}, extend = data.extend || []; if(!(extend instanceof Array)) extend = [extend]; // define constructor var constructor; if(data.construct) { constructor = data.construct; } else if(extend.length) { constructor = function() { for(var i=0; i<extend.length; i++) { extend[i].apply(this, arguments); } } } else { constructor = function() {}; } // prototype for our class. var proto = {}; delete data.construct; delete data.abstracts; delete data.statics; delete data.extend; // borrow methods from parent classes for(var i=0; i<extend.length; i++) { var parent = extend[i]; // static constructor if( typeof parent.construct == "function") parent.construct.call(constructor); // copy static methods for(var p in parent) { if (typeof parent[p] != "function" || p == "construct") // copy only functions continue; constructor[p] = parent[p]; } // Copy prototype methods for(var p in parent.prototype) { if (typeof parent.prototype[p] != "function" || p == "constructor") continue; proto[p] = parent.prototype[p]; } } // build abstract static methods if(statics.abstracts) { for(var p=0; p<statics.abstracts.length; p++) { proto[ statics.abstracts[p] ] = function() { throw p + ' is static abstract method' }; } } // build abstract prototype methods for(var p=0; p<abstracts.length; p++) { proto[ abstracts[p] ] = function() { throw p + ' is abstract method' }; } // internal methods proto.instanceOf = function(_class) { if(arguments.length > 1) { var res = true; for(var i=0; i<arguments.length; i++) res = res && this.instanceOf(arguments[i]); return res; } if(constructor === _class) return true; for(var i=0; i<extend.length; i++) { if( extend[i].prototype.instanceOf.call(this, _class) ) return true; } return _class === Object; }; // rest of data are prototype methods for(var p in data) { if (typeof data[p] != "function") // copy only functions continue; proto[p] = data[p]; } // static functions of class for(var p in statics) { if (typeof statics[p] != "function") // copy only functions continue; constructor[p] = statics[p]; } // static constructor if( typeof statics.construct == "function") statics.construct.call(constructor); // proto.constructor = constructor; constructor.prototype = proto; constructor.fn = proto; // short case // Finally, return the constructor function return constructor; } 

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


All Articles