📜 ⬆️ ⬇️

Using classes in javascript

Methods of creating classes in JavaScript have been discussed more than once throughout the entire Internet and on Habré including, I decided to find a slightly different approach to creating classes that is close to real classes. One important difference from the other implementations described in many articles is the ability to create accessors (setter's / getter's). Which will work not only in modern browsers, but also in long-lived IE below the 9th version. Read about it below.

To begin with, I will describe how to create the classes of the types we need, classes can have the usual public properties, private properties and static properties.

Creating classes


To create a class, simply declare the class name and assign an object to it.
An example of creating an empty class:
classes.Class( "EmptyClass", {} ); //    classes.EmptyClass alert( classes.EmptyClass ); //  [class EmptyClass] 

As you already understood the creation of a class does not require a huge amount of writing code.

To create a class with private properties, it is enough to declare the second parameter not an object but a function in which the class object will be returned.
')
An example of a class with private properties:
 classes.Class( "PrivatePropertyClass", function(){ //   / var privateProp = "tratata", twoPrivateProp = "lalala"; //     return { } }); //    var privateTest = new classes.PrivatePropertyClass(); //     alert( privateTest.privateProp ); //  undefined 

Classes can be created not only in the context of classes, but also in any other.

For example, I will show several ways of how this is done, you can choose any way that is acceptable for you, without limiting yourself to anything.

Here are ways to create a class in any convenient context:
 //      window classes.Class.call( window, "GlobalClass", {} ); //      var CurrentContextClass = classes.Class( {} ); //           //     classes c  ClassesContextClass var CurrentContextClass = classes.Class( "ClassesContextClass", {} ); 

On this, with the creation of classes, we’ll actually finish; I don’t need other ways.

Work with classes


Now I will show how to work with classes, the principle of their work is no different for example from the classes existing in PHP. "It can not be like that!" You ask, yes, of course it cannot. There are some subtleties here, of course there is no possibility of creating interfaces, abstraction and other full-fledged charms of the PLO. But using existing possibilities, the programmer can safely use the knowledge of class programming, the behavior of classes is predictable, the context does not run there / here, but has the same instance of the generated class.

To begin with, let's create a simple class that will display information in a browser window.
 classes.Class( "Debug", function() { //   var //       BODY   body = null, //           body   cache = []; return { //  ,        //  callback   ,     constructor: function( callback ) { //          var listener = window.addEventListener ? [ "addEventListener", "" ] : [ "attachEvent", "on" ]; //       , //      if ( document.readyState === "complete" ) { //      ,     //       BODY body = document.body; //        //     if ( callback && typeof callback === "function" ) { callback.call( this ); } //      return; } //        callback' var self = this; //   ,       window[ listener[ 0 ] ]( listener[ 1 ] + "load", function() { //     ,     //      BODY body = document.body; //        ,   . for( var i = 0; i < cache.length; i++ ) { body.appendChild( cache[ i ] ); cache[ i ] = null; } //   cache.length = 0; //        //     if ( callback && typeof callback === "function" ) { callback.call( self ); } // bubbling - : http://learn.javascript.ru/bubbling-and-capturing }, false ); }, //             write: function() { //  DIV      var div = document.createElement( "DIV" ), //       ,   //       TRUE  //          DOM // . isPlainText = arguments.length ? arguments[ arguments.length - 1 ] === true : false, //      dataArray = Array.prototype.slice.call( arguments ); //       HTML   DOM  if ( isPlainText && dataArray.pop() ) { //       ,   //            DOM div.appendChild( document.createTextNode( dataArray.join( ", " ) ) ); } else { //        DOM . div.innerHTML = dataArray.join( ", " ); } //           if ( body ) { //        BODY  body.appendChild( div ); } else { //          BODY cache[ cache.length ] = div; } } } }); 

Here we have created our full-fledged class, in it we used the approach with private properties, this class doesn’t do anything particularly clever, but simply displays the text in a browser window, while waiting for the document to load completely so that an error does not occur.

For example, we can now create an instance of this class and print our first message.
 var debug = new classes.Debug(); debug.write( "  <var>classes.Debug</var>  !" ); 

“Nothing special!” You say, the usual unnecessary creation of classes in a different way. Yes, I will answer you, there is nothing particularly abstruse here, but the goodies have not yet been told.

Inheritance


Let's now create our second class that will inherit the properties of our Debug class. Our new class will be the usual button that will change color when you click on it.
 //   ButtonClass      Debug classes.Class( "ButtonClass extends Debug", function() { //   var mouseState = 0, //   ,  DOM  button = null; //   function switchState( type ) { //     if ( type === 1 ) { mouseState++; //             button.style.backgroundColor = "green"; return; } else if ( type === 2 ) { mouseState--; } else { mouseState = 0; } //     button.style.backgroundColor = "red"; } return { //     constructor: function() { //     button = document.createElement( "SPAN" ); //      button.style.border = "1px solid blue"; button.style.color = "white"; button.style.textAlign = "center"; button.style.backgroundColor = "red"; button.style.borderRadius = "5px"; button.style.padding = "4px"; button.style.cursor = "default"; //      button.innerHTML = "  "; //    -   Debug //            //  ,   Debug      this.parent.constructor( function() { //      var self = this; //      DOM document.body.appendChild( button ); //     IE      button.onselectstart = function() { return false; } //     button.onmousedown = function( e ) { //     var e = e || window.event; //   ,    switchState( 1 ); //        //     . if ( e.preventDefault ) { e.preventDefault(); } else { e.returnValue = false; } } //      button.onmouseup = function() { //   , -  switchState( 2 ); //         if ( mouseState === 0 ) { //      //     self.click(); } } //       button.onmouseout = function() { //     ,    if ( mouseState && mouseState++ ) { //       switchState( 2 ); } } //        button.onmouseover = function() { //     ,   if ( mouseState && mouseState-- ) { //      switchState( 1 ); } } //          var handler = window.document.onmouseup; window.document.onmouseup = function( e ) { //        switchState(); //       if ( handler ) { handler.call( window, e ); } } }); }, //     DOM    node: function() { return button; }, //    ,       //          . click: function() { } } }); 

And so we created a new class ButtonClass that inherits the properties of the Debug class. As you have already noticed, inheritance is done by adding the word extends followed by the name of the class with which we want to inherit the properties.

This is not the only mode of inheritance, it can be done in another way, for example:
 var Child = classes.Class( classes.Debug, {} ); 


As we see the class. Child became the heir of the classes. Class.

And now let's try our written button.
 //    var button = new classes.ButtonClass(); //        button.click = function() { //  write     Debug this.write( "         " ); } //         :) button.write( "  <var>classes.ButtonClass</var>  !" ); 

As you see, we have a fully working button, maybe it’s not beautiful, but these are trifles. You can always change the style, the name of the button. This is just a small example of how projects can be implemented in classes.

Setters / Getters


And now let's move on to the very goodies, which are so lacking due to limitations, as you know Internet Explorer below version 9 does not allow working normally with getters / setters, this is a huge minus in project development. Yes, of course, the possibilities of the language do not diminish from this, and the possibility of writing programs, too. But I still tried to implement them in the current classes, I can rather call it some “magic getter / setter”, there is no need to hang for each property any defineProperty, but simply specify which properties should be intercepted.

Let us expand our class of buttons with you and create a kind of super class that will give you the opportunity to change the text of the button through getters / setters. In this class, we will not use constructors or private methods, but only create a property that will be intercepted by a magic getter / setter
 classes.Class( "SuperButtonClass extends ButtonClass", { //        / //  ,         //             //              $text: null, //  ,         //    property     ,   //       ,      //    set__: function( property, value ) { //             this.write( " SETTER   <var>" + property + "</var>   <var>" + value + "</var>" ); //    text if ( property === "text" ) { //        this.node().innerHTML = value; } }, //  ,           //  ,             // ,     . get__: function( property ) { //             this.write( " GETTER   <var>" + property + "</var>" ); //    text if ( property === "text" ) { //      return this.node().innerHTML; } } }); 

Here we have created a super class for the button, which simply gives the opportunity to change the text of the button by simply assigning the text property, the value we need, of course, not all features of the getters / setters can be used in any conditions, with any data type, etc. .

And now let's look at what we did:
 //      var superButton = new classes.SuperButtonClass(); //  ,       //        superButton.write( "    : <var>" + superButton.text + "</var>" ); //              //         superButton.text = "   "; //           superButton.write( "  <var>classes.SuperButtonClass</var>  !" ); 

All the examples described you can see in action here on this link .

Static properties


It makes no sense to describe static properties in particular, as everyone knows they are added in the usual way known to all:
 classes.SuperButtonClass.NEW_STATIC = " "; 


Finally, I want to draw attention to the fact that when referring to parent methods, you do not need to explicitly specify the context. I think you notice that I call the Debug class constructor from our button class, the usual call to this.parent.constructor () while the debug class will already have the context of the last descendant, that is, the initiator of the classes. You do not need to call parent methods through the well-known call, apply, etc. Simply call this.parent.parentMethod (args); and the relative will work with the child context.

I also add that the creation of additional getters / setters in an already existing instance of the class, of course, cannot be added in a browser such as IE below version 9. Therefore, there are small restrictions on the dynamics, so when using getters / setters in the descendant classes and / or its heirs, it will not be possible to add dynamically any properties. But this restriction applies only to IE below version 9, and if there is at least one getter / setter.

Suppose we want to create an additional property on an instance of the class SuperButtonClass or its descendants, which we do not yet have. But in the future they will be in any case. That attempt to create will lead to an error in the IE below version 9, because the object with setters / getters is generated via VBScript and there as you know there is a restriction that does not allow to declare an additional property if it is not explicitly specified.

But at the instance of the class ButtonClass we can easily create additional properties, since we do not use setters / getters for this class and its descendants.

I also want to add that the native instanceof will not respond correctly to these classes, so for these cases I added the classes.instanceOf method to check if the instance belongs to the class we need in our case:
 alert( classes.instanceOf( superButton, classes.Debug ) ); //  TRUE 


That's all about classes in this article, in the future there will probably be some additions
changes and of course error correction. Although during the development of their identified was not.

Have a nice class build, good luck and thank you for your attention and future criticism!

Download the library for working with classes, you can link: http://code.spb-piksel.ru/?classes.latest.zip
I will also post it on GitHub: https://github.com/devote where you can download not only her, but also my other projects.

UPD: As Ashot noted in one of the comments, there are many libraries already invented for building classes in JavaScript. But this library is different from all of them in that it has the ability to create accessors (setter's / getter's). I did not find such an implementation in any of the mentioned libraries. Accessors work not only in modern browsers, but also in IE below version 9. This is what I want to distinguish my implementation from other class creation implementations.

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


All Articles