JavaScript classes

I understand the basic JavaScript pseudo-classes:

function Foo(bar) { this._bar = bar; } Foo.prototype.getBar = function() { return this._bar; }; var foo = new Foo('bar'); alert(foo.getBar()); // 'bar' alert(foo._bar); // 'bar' 

I also understand the module template, which can emulate encapsulation:

 var Foo = (function() { var _bar; return { getBar: function() { return _bar; }, setBar: function(bar) { _bar = bar; } }; })(); Foo.setBar('bar'); alert(Foo.getBar()); // 'bar' alert(Foo._bar); // undefined 

But for both of these patterns, there are properties other than OOP. The first does not provide encapsulation. The latter does not give an instantiation. Both patterns can be modified to support pseudo-inheritance.

I would like to know if there is any template that allows:

  • Inheritance
  • Encapsulation (support for "private" properties / methods)
  • Activation (may have several instances of a “class”, each with its own state)
+43
javascript oop design-patterns
Sep 26 '12 at 21:05
source share
8 answers

what about it:

 var Foo = (function() { // "private" variables var _bar; // constructor function Foo() {}; // add the methods to the prototype so that all of the // Foo instances can access the private static Foo.prototype.getBar = function() { return _bar; }; Foo.prototype.setBar = function(bar) { _bar = bar; }; return Foo; })(); 

And now we have instantiation, encapsulation and inheritance.
But there is still a problem. The variable is private static because it is shared across all instances of Foo . Quick demo:

 var a = new Foo(); var b = new Foo(); a.setBar('a'); b.setBar('b'); alert(a.getBar()); // alerts 'b' :( 

A better approach would be to use conventions for private variables: any personal variable should begin with an underscore. This convention is well known and widely used, so when another programmer uses or modifies your code and sees a variable starting with an underscore, he will know that it is closed for internal use and he will not change it.
Corresponding here using this convention:

 var Foo = (function() { // constructor function Foo() { this._bar = "some value"; }; // add the methods to the prototype so that all of the // Foo instances can access the private static Foo.prototype.getBar = function() { return this._bar; }; Foo.prototype.setBar = function(bar) { this._bar = bar; }; return Foo; })(); 

Now we have an instance, inheritance, but we have lost encapsulation in favor of conventions:

 var a = new Foo(); var b = new Foo(); a.setBar('a'); b.setBar('b'); alert(a.getBar()); // alerts 'a' :) alert(b.getBar()); // alerts 'b' :) 

but private vars are available:

 delete a._bar; b._bar = null; alert(a.getBar()); // alerts undefined :( alert(b.getBar()); // alerts null :( 
+71
Sep 26 '12 at 21:16
source share

Javascript is definitely OOP. You always have polymorphism, but you must sacrifice the encapsulation or instantiation you encounter.

Try this to just brush up on your options. http://www.webmonkey.com/2010/02/make_oop_classes_in_javascript/ Also an old bookmarked question: Is object oriented JavaScript?

+3
Sep 26
source share

Closing is your friend!

Just add the following tiny function to the top-level namespace and you are ready for OOP, complete with

  • encapsulation with static and instances, private and public variables and methods
  • inheritance
  • class level injection (e.g. for Singleton services)
  • no limits, no framework, just old javascript



 function clazz(_class, _super) { var _prototype = Object.create((_super || function() {}).prototype); var _deps = Array.isArray(_class) ? _class : [_class]; _class = _deps.pop(); _deps.push(_super); _prototype.constructor = _class.apply(_prototype, _deps) || _prototype.constructor; _prototype.constructor.prototype = _prototype; return _prototype.constructor; } 

The above function simply connects the prototype of this class and a possible parent constructor and returns the resulting constructor, ready to instantiate.

Now you can naturally declare your base classes (that is, extend {}) in several lines of code, complete with static, instances, public and private properties and methods:

 MyBaseClass = clazz(function(_super) { // class closure, 'this' is the prototype // local variables and functions declared here are private static variables and methods // properties of 'this' declared here are public static variables and methods return function MyBaseClass(arg1, ...) { // or: this.constructor = function(arg1, ...) { // local variables and functions declared here are private instance variables and methods // properties of 'this' declared here are public instance variables and methods }; }); 

Class extension? All the more natural:

 MySubClass = clazz(function(_super) { // class closure, 'this' is the prototype // local variables and functions are private static variables and methods // properties of this are public static variables and methods return function MySubClass(arg1, ...) // or: this.constructor = function(arg1, ...) { // local variables and functions are private instance variables and methods _super.apply(this, arguments); // or _super.call(this, arg1, ...) // properties of 'this' are public instance variables and methods }; }, MyBaseClass); // extend MyBaseClass 

In other words, pass the constructor of the parent class to the clazz function and add _super.call(this, arg1, ...) to the constructor of the child class, which calls the constructor of the parent class with the necessary arguments. As with any standard inheritance scheme, the call to the parent constructor must be the first in the child constructor.

Please note that you can explicitly specify the constructor using this.constructor = function(arg1, ...) {...} or this.constructor = function MyBaseClass(arg1, ...) {...} if you you need simple access to the constructor from the code inside the constructor or even just return the constructor with return function MyBaseClass(arg1, ...) {...} as in the above code. Depending on what you like best.

Just create objects from such classes, as usual, from the constructor: myObj = new MyBaseClass();

Note that closures perfectly encapsulate all the functionality of a class, including its prototype and constructor, providing a natural namespace for static and instances, private and public properties, and methods. The code inside the class closure is completely free of restrictions. No framework, no limitations, just old Javascript. Closure rule!

Oh, and if you want to embed single-leaf dependencies (e.g. services) in your class (i.e. prototype), clazz will do this for you on la AngularJS:

 DependentClass = clazz([aService, function(_service, _super) { // class closure, 'this' is the prototype // the injected _service dependency is available anywhere in this class return function MySubClass(arg1, ...) // or: this.constructor = function(arg1, ...) { _super.apply(this, arguments); // or _super.call(this, arg1, ...) // the injected _service dependency is also available in the constructor }; }], MyBaseClass); // extend MyBaseClass 

As you can see from the above code, to inject singleton into a class, just put the closure of the class as the last record in the array with all its dependencies. Also add the appropriate parameters to close the class before the _super parameter and in the same order as in the array. clazz will insert dependencies from the array as arguments into the class closure. Dependencies are then available anywhere in the class closure, including constructor.

In fact, since dependencies are introduced into the prototype, they are available for static methods even before any object is created from the class. It is very convenient for connecting your applications or devices and end-to-end tests. It also eliminates the need to blow singletones into constructors that would otherwise unnecessarily knock down constructor code.

Check out this script: http://jsfiddle.net/5uzmyvdq/1/

Feedback and suggestions are most welcome!

+3
Mar 22 '15 at 9:58
source share

I have been thinking about this particular subject lately and about the limitations of various approaches. The best solution I could come up with is below.

It seems to solve problems with inheritance, instantiation and ecapsulation (at least from tests in Google Chrome v.24), although it is probably worth the cost of memory usage.

 function ParentClass(instanceProperty) { // private var _super = Object.create(null), privateProperty = "private " + instanceProperty; // public var api = Object.create(_super); api.constructor = this.constructor; api.publicMethod = function() { console.log( "publicMethod on ParentClass" ); console.log( privateProperty ); }; api.publicMethod2 = function() { console.log( "publicMethod2 on ParentClass" ); console.log( privateProperty ); }; return api; } function SubClass(instanceProperty) { // private var _super = ParentClass.call( this, instanceProperty ), privateProperty = "private sub " + instanceProperty; // public var api = Object.create(_super); api.constructor = this.constructor; api.publicMethod = function() { _super.publicMethod.call(this); // call method on ParentClass console.log( "publicMethod on SubClass" ); console.log( privateProperty ); } return api; } var par1 = new ParentClass(0), par2 = new ParentClass(1), sub1 = new SubClass(2), sub2 = new SubClass(3); par1.publicMethod(); par2.publicMethod(); sub1.publicMethod(); sub2.publicMethod(); par1.publicMethod2(); par2.publicMethod2(); sub1.publicMethod2(); sub2.publicMethod2(); 
0
Feb 19 '13 at 13:21
source share

One problem with many JS classes is that they do not protect their fields and methods, which means that anyone who uses it can accidentally replace a method. For example, the code:

 function Class(){ var name="Luis"; var lName="Potter"; } Class.prototype.changeName=function(){ this.name="BOSS"; console.log(this.name); }; var test= new Class(); console.log(test.name); test.name="ugly"; console.log(test.name); test.changeName(); test.changeName=function(){ console.log("replaced"); }; test.changeName(); test.changeName(); 

will output:

 ugly BOSS replaced replaced 

As you can see, the changeName function gets higher. The following code will protect the methods and fields of the class, and getters and setters will be used to access them, making it a more “normal” class found in other languages.

 function Class(){ var name="Luis"; var lName="Potter"; function getName(){ console.log("called getter"); return name; }; function setName(val){ console.log("called setter"); name = val }; function getLName(){ return lName }; function setLName(val){ lName = val; }; Object.defineProperties(this,{ name:{ get:getName, set:setName, enumerable:true, configurable:false }, lastName:{ get:getLName, set:setLName, enumerable:true, configurable:false } }); } Class.prototype.changeName=function(){ this.name="BOSS"; }; Object.defineProperty(Class.prototype, "changeName", { writable:false, configurable:false }); var test= new Class(); console.log(test.name); test.name="ugly"; console.log(test.name); test.changeName(); test.changeName=function(){ console.log("replaced") }; test.changeName(); test.changeName(); 

It is output:

 called getter Luis called setter called getter ugly called setter called setter called setter 

Now your class methods cannot be replaced with random values ​​or functions, and the code in getters and setters is always executed when trying to read or write to a field.

0
Sep 04 '14 at 22:41
source share

JavaScript classes are introduced in ECMAScript 6 and are syntactic sugar over existing inheritance based on JavaScript prototypes. The class syntax does not represent a new object-oriented inheritance model for JavaScript. JavaScript classes provide a much simpler syntax for creating objects and handling inheritance.

You can see more at this link Mozilla Community

Github

0
Jan 29 '16 at 14:46
source share

This closure allows you to instantiate and encapsulate, but does not inherit.

 function Foo(){ var _bar = "foo"; return { getBar: function() { return _bar; }, setBar: function(bar) { _bar = bar; } }; }; a = Foo(); b = Foo(); a.setBar("bar"); alert(a.getBar()); // "bar" alert(b.getBar()); // "foo" 
-one
Jan 03 '14 at 3:08
source share



All Articles