class-advanced

2.1.3 • Public • Published

Javascript Class (Class.js)

Latest Stable Version License

Universal JS library for prototyped classes - extending, constructor, static and dynamic elements, parent methods calls, self reflection and much more. For all browsers, Node.js and Windows Script Host.

INSTALATION

npm install oop-class

DOWNLOAD

<!-- for production: -->
<script type="text/javascript" src="https://tomflidr.github.io/class.js/builds/2.1.3/class.min.js"></script>
 
<!-- for development with JSDocs comments for IDE: -->
<script type="text/javascript" src="https://tomflidr.github.io/class.js/builds/2.1.3/class.dev.js"></script>

DEMOS

Features

  • very fast, effective, supersmall - all in 6.5 KB - minimized, 2.4 KB - gzipped
  • multi environment:
    • all browsers (MSIE6+, Safari, Opera, Chrome)
    • Node.js
    • WSH (Windows Script Host)
    • Adobe (only old archived version 0.6)
  • syntax customization - any declaration keyword or internal class keyword shoud be customized
  • inspired by PHP OOP, Ext.JS and Prototype.JS syntax
  • documented with JSDocs comments
  • Function.prototype.bind polyfill included
  • possibility to define:
    • Static elements
    • parent class by Extend keyword
    • Constructor method
    • all other elements as dynamic elements
  • possibility to call any dynamic and static parent method anywhere by:
    • this.parent(arguments); // in static and dynamic functions
    • this.parent(param1, param2); // in static and dynamic functions
    • this.parent.anyStaticMethod(param1, param2); // in static functions
    • this.parent.anyDynamicMethod(param1, param2); // in dynamic functions
    • this.parent.apply(this, [param1, param2]); // in static and dynamic functions
    • this.parent.anyStaticMethod.apply(this, [param1, param2]); // in static functions
    • this.parent.anyDynamicMethod.apply(this, [param1, param2]); // in dynamic functions
  • posibility to get current class definition by:
    • this.self; // without the need to know class name itself
    • this.static; // without the need to know class name itself
    • this.self context (static class definition) is changed in each defined dynamic and static method into value coresponded with original definition place
    • this.static context (static class definition) is not changed and it all time coresponds to original instance context - like Late Static Bindings in PHP OOP
  • posibility to get class name / fullname / namespace (only if class is defined by Class.Define();) by:
    • this.self.Fullname; or this.static.Fullname;
    • this.self.Name; or this.static.Name;
    • this.self.Namespace; or this.static.Namespace;
  • posibility to create instance by:
    • classic Javascript new keyword: var instance = new ClassName(param1, param2);
    • class name string with Class.Create(); method: var instance = Class.Create('ClassName', param1, param2);
  • inheritance checking by javascript 'instanceof' keyword
  • posibility to create anonymous classes like:
    • new Class({Constructor:function(text){console.log(text)}})("It works!");
  • Visual Studio - Go To Definition (F12) support - for current level objects and parent methods

1. Basic Class - Animal

// Declare not named class by Class(...) 
// call into custom variable 'Animal':
var Animal = Class({
    Constructor: function (name, sound) {
        this.name = name;
        this.sound = sound;
    },
    name: '',
    sound: '',
    MakeNoise: function () {
        console.log(this.sound);
    },
    IntroduceYourself: function () {
        console.log(
            "People call me '{0}'.".format(this.name)
        );
    }
});
 
// Create instance:
var dog = new Animal('Charlie', 'Wrr haf!');
 
// 'Wrr haf!'
dog.MakeNoise();
 
// 'People call me 'Charlie'.'
dog.IntroduceYourself();

2. Class Dog And Cat Extends Animal

// Declare named class by Class.Define(...) 
// call into global memory space as 'Animal'
Class.Define('Animal', {
    Static: {
        GetInstance: function () {
            // this.static contains a child class definition
            // as later static binding in PHP normaly works
            return new this.static(arguments);
        }
    },
    Constructor: function (name, sound) {
        this.name = name;
        this.sound = sound;
    },
    name: '',
    sound: '',
    MakeNoise: function () {
        console.log(this.sound);
    },
    IntroduceYourself: function () {
        console.log(
            "People call me '{0}'.".format(this.name)
        );
    },
    DefineYourself: function (asdf) {
        console.log(
            "Globaly, I'm an '{0}'.".format(this.self.Name)
            + '<br />' +
            "More precisely, I'm a '{0}'.".format(this.static.Name)
            + '<br />' +
            "I live like an '{0}'.".format(this.static.Namespace)
            + '<br />' +
            "My namespace is '{0}'.".format(this.static.Fullname)
        );
    },
    TellYourStory: function () {
        this.MakeNoise();
        this.DefineYourself();
    }
});
 
// Declare named classes by Class.Define(...) call 
// into global memory space as 'Animal.Dog' and 'Animal.Cat'
// as extended classes from 'Animal' class.
Class.Define('Animal.Dog', {
    Extend: Animal,
    TellYourStory: function () {
        this.MakeNoise();
        this.IntroduceYourself();
        this.DefineYourself();
        console.log("But I'm the best friend of human.")
    }
});
Class.Define('Animal.Cat', {
    Extend: Animal,
    TellYourStory: function () {
        this.MakeNoise();
        this.IntroduceYourself();
        this.DefineYourself();
        console.log(
            "I don't care about people, but sometimes <br />"+
            "they have something very good to eat."
        );
    }
});
 
// Create instances (all ways are creating an instance):
var creature = Class.Create("Animal", "Creature", "Rrroooaaarrr!")
var dog = new Animal.Dog("Charlie", "Wrr haf!");
var cat = Animal.Cat.GetInstance('Suzy', 'Pchchchchch!');
 
 
 
// 'Rrroooaaarrr!'
 
// Globaly, I'm an 'Animal'.
// More precisely, I'm a 'Animal'.
// I belong to namespace ''.
// My full description is 'Animal'.
creature.TellYourStory();
 
 
console.log("-------------------");
 
 
// 'Wrr haf!'
// People call me 'Charlie'.
 
// Globaly, I'm an 'Animal'.
// More precisely, I'm a 'Dog'.
// I live between 'Animal'.
// My type is 'Animal.Dog'.
 
// But the best friend of human.
dog.TellYourStory();
 
 
console.log("-------------------");
 
 
// Pchchchchch! [String]
// People call me 'Suzy'. [String]
 
// Globaly, I'm an 'Animal'.
// More precisely, I'm a 'Cat'.
// I live between 'Animal'.
// My type is 'Animal.Cat'. [String]
 
// I don't care about people, but sometimes 
// they have something very good to eat.
cat.TellYourStory();
 
 
console.log("-------------------");
 
console.log(dog instanceof Animal); // true
console.log(dog instanceof Animal.Dog); // true
console.log(dog instanceof Animal.Cat); // false
console.log(dog instanceof Date); // false
 
console.log("-------------------");
 
console.log(dog.static.Name); // 'Dog'
console.log(dog.static.Fullname); // 'Animal.Dog'
console.log(dog.static.Namespace); // 'Animal'
console.log(dog.static.Extend.Name); // 'Animal'
console.log(dog.static.Extend.Fullname); // 'Animal'
console.log(dog.static.Extend.Namespace); // ''
 
console.log("-------------------");
 
console.log(dog.static === Animal.Dog); // true
console.log(dog.static.Extend === Animal); // true
console.log(dog.static.prototype === Animal.Dog.prototype); // true
console.log(dog.static.Extend.prototype === Animal.prototype); // true
 
console.log("-------------------");
 
console.log(typeof Animal.Dog); // 'function'
console.log(typeof dog); // 'object'
console.log(dog.toString()); // '[object Animal.Dog]'

4. Three Extended Classes With Static Members

Class.Define('Person', {
    Static: {
        Store: [],
        Count: 0,
        Register: function (person) {
            // in Static block:
            // - this context represents static context environment
            this.Store[person.id] = person;
            this.Count += 1;
        }
    },
    Constructor: function (id, name) {
        // in Constructor and dynamic methods:
        // - this context represents dynamic context environment
        this.id = id;
        this.name = name;
        this.self.Register(this);
    },
    id: 0,
    name: ''
});
 
Class.Define('Employe', {
    Extend: Person,
    Constructor: function (id, name, salary) {
        this.parent(id, name);
        this.salary = salary;
    },
    salary: 0,
    GetInfo: function () {
        return this.static.Name + " - name: " + this.name
            + ", id: " + this.id + ", salary: " + this.salary;
    }
});
 
Class.Define('Manager', {
    Extend: Employe,
    Constructor: function (id, name, salary, idSecretary) {
        this.parent(id, name, salary);
        this.idSecretary = idSecretary;
    },
    idSecretary: 0,
    GetInfo: function () {
        var parentInfo = this.parent();
        return parentInfo + ",<br />"
            + "&nbsp;&nbsp;&nbsp;" + "- secretary: "
            + this.self.Store[this.idSecretary].GetInfo();
    }
});
 
// create class instance in standard way
var prManager = new Manager(0, 'Douglas Bridges', 50000, 1);
 
// create class instance by string as first argument, 
// all constructor params as second argument array
var secretary = Class.Create('Employe', 1, 'Janet Williams', 30000);
 
// 'Employe - name: Janet Williams, id: 1, salary: 30000'
console.log(secretary.GetInfo());
 
// 'Manager - name: Douglas Bridges, id: 0, salary: 50000,
// - secretary: Employe - name: Janet Williams, id: 1, salary: 30000'
console.log(prManager.GetInfo());
 
// Primitive values are not linked,
// so real count of registered persons
// is written in Person.Count memory space
console.log(Person.Count); // 2
console.log(Employe.Count); // 0
console.log(Manager.Count); // 0
 
// Nonprimitive values are lined as references,
// so registered persons store is written 
// in Person.Count memory space and two another links are created
console.log(Person.Store.length); // 2
console.log(Employe.Store.length); // 2
console.log(Manager.Store.length); // 2

5. Three Controller Classes And Different Behaviour In Actions

// System controller class - parent for all controllers:
Class.Define('Controller', {
    Static: {
        Dispatch: function (path, actionName) {
            new this.static(path)[actionName + 'Action']().Render();
        }
    },
    Constructor: function (path) {
        this.path = path;
    },
    path: null,
    Render: function () {
        console.log(JSON.stringify(this.path));
    }
});
 
// Front controller class - parent for all front controllers:
Class.Define('Controller.Front', {
    Extend: Controller,
    prepareView: function () {
        this.view = {
            path: this.path,
            agent: navigator.appName,
            lang: navigator.language
        };
    },
    view: null,
    Render: function () {
        console.log(
            JSON.stringify(this.view, null, "  ")
        );
    }
});
 
// Specific controller class for text pages,
// this controller will be dispatched 4 times:
Class.Define('Controller.Front.Default', {
    Extend: Controller.Front,
    prepareView: function () {
        this.parent(arguments);
        this.view.content = "You are here: '{0}'."
            .format(this.view.path.substr(1));
        this.view.layout = 'two-columns';
        this.view.leftMenu = [
            'About', 'Partners', 'Contacts'
        ];
    },
    HomeAction: function () {
        /*****************************************************/
        /* You can call parent method directly from         **/
        /* any other method to skip current implementation! **/
        this.parent.prepareView();
        /*****************************************************/
        this.view.content = 'Welcome to our website!';
        this.view.layout = 'one-column';
        return this;
    },
    DefaultAction: function () {
        this.prepareView();
        return this;
    },
    ContactsAction: function () {
        this.prepareView();
        this.view.contactMain = 'info@company.com';
        return this;
    }
});
 
 
// Dispatching different requests to different 
// actions with different needs:
 
var ctrlDef = Controller.Front.Default;
 
ctrlDef.Dispatch('/home', 'Home');
ctrlDef.Dispatch('/about-us', 'Default');
ctrlDef.Dispatch('/partners', 'Default');
ctrlDef.Dispatch('/contacts', 'Contacts');

6. Class A, B, C And Parent Methods Calls Flows

Class.Define('A', {
    Static: {
        Create: function (one, two, three) {
            console.log(this.self.Name + '::Create(' + one + ',' + two + ',' + three + ')');
            return Class.Create(this.static.Name, arguments);
        },
        FirstStatic: function (a, b, c) {
            console.log(this.self.Name + '::FirstStatic(' + a + ',' + b + ',' + c + ')');
        }
    },
    Constructor: function (one, two, three) {
        console.log(this.self.Name+'->Constructor('+one+','+two+','+three+')');
    },
    FirstDynamic: function (f, g, h) {
        console.log(this.self.Name+'->FirstDynamic('+f+','+g+','+h+')');
        return this;
    },
    SecondDynamic: function (x, y, z) {
        console.log(this.self.Name+'->SecondDynamic('+x+','+y+','+z+')');
        return this;
    },
    ThirdDynamic: function (x, y, z) {
        console.log(this.self.Name+'->ThirdDynamic('+x+','+y+','+z+')');
        return this;
    }
});
 
Class.Define('B', {
    Extend: A,
    Static: {
        FirstStatic: function (a, b, c) {
            console.log("this is never called");
        },
        SecondStatic: function (a, b, c) {
            console.log(this.self.Name+'::SecondStatic('+a+','+b+','+c+')');
            this.parent.FirstStatic(a, b, c);
        }
    },
    Constructor: function (one, two, three) {
        console.log(this.self.Name+'->Constructor('+one+','+two+','+three+')');
        this.parent(arguments);
    },
    FirstDynamic: function (x, y, z) {
        console.log(this.self.Name+'->FirstDynamic('+x+','+y+','+z+')');
        this.ThirdDynamic(x, y, z);
        return this;
    },
    ThirdDynamic: function (x, y, z) {
        console.log(this.self.Name+'->ThirdDynamic('+x+','+y+','+z+')');
        this.parent.ThirdDynamic(x, y, z);
        return this;
    }
});
 
Class.Define('C', {
    Extend: B,
    Static: {
        SecondStatic: function (a, b, c) {
            console.log("this is never called");
        },
        ThirtStatic: function (a, b, c) {
            console.log(this.self.Name + '::ThirtStatic(' + a + ',' + b + ',' + c + ')');
            this.parent.SecondStatic(a, b, c);
        }
    },
    one: 0,
    two: 0,
    three: 0,
    Constructor: function (one, two, three) {
        this.one = one;
        this.two = two;
        this.three = three;
        console.log(this.self.Name+'->Constructor('+one+','+two+','+three+')');
        this.parent(arguments);
    },
    FirstDynamic: function (f, g, h) {
        console.log(this.self.Name+'->FirstDynamic('+f+','+g+','+h+')');
        this.parent.SecondDynamic(f, g, h);
        return this;
    },
    SecondDynamic: function (m, n, o) {
        console.log(this.self.Name+'->SecondDynamic('+m+','+n+','+o+')');
        this.ThirdDynamic(m, n, o);
        return this;
    },
    ThirdDynamic: function (x, y, z) {
        console.log(this.self.Name+'->ThirdDynamic('+x+','+y+','+z+')');
        this.parent.FirstDynamic(x, y, z);
        return this;
    }
});
 
 
/**
This code flows through methods:
    C::ThirtStatic(a,b,c)
        B::SecondStatic(a,b,c)
            A::FirstStatic(a,b,c)
    C::Create(1,2,3)
*/
C.ThirtStatic('a', 'b', 'c');
 
 
/**
This code flows through methods:
    C->Constructor(1,2,3)
        B->Constructor(1,2,3)
            A->Constructor(1,2,3)
*/
var c = C.Create(1, 2, 3)
    /**
    This code flows through methods:
        C->FirstDynamic(f,g,h)
                A->SecondDynamic(f,g,h)
    */
    .FirstDynamic('f', 'g', 'h')
    /**
    This code flows through methods:
        C->SecondDynamic(m,n,o)
        C->ThirdDynamic(m,n,o)
            B->FirstDynamic(m,n,o)
            B->ThirdDynamic(m,n,o)
                A->FirstDynamic(m,n,o)
    */
    .SecondDynamic('m', 'n', 'o')
    /**
    This code flows through methods:
        C->ThirdDynamic(x,y,z)
            B->FirstDynamic(x,y,z)
                A->ThirdDynamic(x,y,z)
    */
    .ThirdDynamic('x', 'y', 'z');
 
console.log(c.toString()); // [object C]

7. Syntax Customization

// syntax customization at app start:
Class.define = Class.Define;
Class.create = Class.Create;
Class.getByName = Class.GetByName;
Class.CustomizeSyntax({
    GetClassUid : 'getClassUid',
    GetInstanceUid : 'getInstanceUid',
    Inherited : 'inherited',
    Extend : 'extend',
    Static : 'static',
    // for 'constructor' is not possible to use javascript 
    // build in function property 'constructor', use different:
    Constructor: 'construct',
    // for 'name' is not possible to use javascript 
    // build in 'Function.name' property, use different:
    Name : 'className',
    Fullname : 'classFullname',
    Namespace : 'classNamespace',
    static : 'static',
    self : 'self',
    parent : 'parent'
});
 
 
// Declare named class by Class.Define(...) 
// call into global memory space as 'Animal'
Class.define('Animal', {
    static: {
        getInstance: function () {
            // this.static contains a child class definition
            // as later static binding in PHP normaly works
            return new this.static(arguments);
        }
    },
    construct: function (name, sound) {
        this.name = name;
        this.sound = sound;
    },
    name: '',
    sound: '',
    makeNoise: function () {
        console.log(this.sound);
    },
    introduceYourself: function () {
        console.log(
            "People call me '{0}'.".format(this.name)
        );
    },
    defineYourself: function () {
        console.log(
            "Globaly, I'm an '{0}'.".format(this.self.className)
            + '<br />' +
            "More precisely, I'm a '{0}'.".format(this.static.className)
            + '<br />' +
            "I belong to namespace '{0}'.".format(this.static.classNamespace)
            + '<br />' +
            "My full description is '{0}'.".format(this.static.classFullname)
        );
    },
    tellYourStory: function () {
        this.makeNoise();
        this.defineYourself();
    }
});
 
// Declare named classes by Class.Define(...) call 
// into global memory space as 'Animal.Dog' and 'Animal.Cat'
// as extended classes from 'Animal' class.
Class.define('Animal.Dog', {
    extend: Animal,
    tellYourStory: function () {
        this.makeNoise();
        this.introduceYourself();
        this.defineYourself();
        console.log("But I'm the best friend of human.")
    }
});
Class.define('Animal.Cat', {
    extend: Animal,
    tellYourStory: function () {
        this.makeNoise();
        this.introduceYourself();
        this.defineYourself();
        console.log(
            "I don't care about people, but sometimes <br />" +
            "they have something very good to eat."
        );
    }
});
 
// Create instances:
var creature = Class.create("Animal", "Creature", "Rrroooaaarrr!")
var dog = new Animal.Dog("Charlie", "Wrr haf!");
var cat = Animal.Cat.getInstance('Suzy', 'Pchchchchch!');
 
 
 
// 'Rrroooaaarrr!'
 
// Globaly, I'm an 'Animal'.
// More precisely, I'm a 'Animal'.
// I belong to namespace ''.
// My full description is 'Animal'.
creature.tellYourStory();
 
 
console.log("-------------------");
 
// 'Wrr haf!'
// People call me 'Charlie'.
 
// Globaly, I'm an 'Animal'.
// More precisely, I'm a 'Dog'.
// I live between 'Animal'.
// My type is 'Animal.Dog'.
 
// But the best friend of human.
dog.tellYourStory();
 
 
console.log("-------------------");
 
 
// Pchchchchch! [String]
// People call me 'Suzy'. [String]
 
// Globaly, I'm an 'Animal'.
// More precisely, I'm a 'Cat'.
// I live between 'Animal'.
// My type is 'Animal.Cat'. [String]
 
// I don't care about people, but sometimes 
// they have something very good to eat.
cat.tellYourStory();
 
 
 
console.log("-------------------");
 
console.log(dog instanceof Animal); // true
console.log(dog instanceof Animal.Dog); // true
console.log(dog instanceof Animal.Cat); // false
console.log(dog instanceof Date); // false
 
console.log("-------------------");
 
console.log(dog.static.className); // 'Dog'
console.log(dog.static.classFullname); // 'Animal.Dog'
console.log(dog.static.classNamespace); // 'Animal'
console.log(dog.static.extend.className); // 'Animal'
console.log(dog.static.extend.classFullname); // 'Animal'
console.log(dog.static.extend.classNamespace); // ''
 
console.log("-------------------");
 
console.log(dog.static === Animal.Dog); // true
console.log(dog.static.extend === Animal); // true
console.log(dog.static.prototype === Animal.Dog.prototype); // true
console.log(dog.static.extend.prototype === Animal.prototype); // true
 
console.log("-------------------");
 
console.log(typeof Animal.Dog); // 'function'
console.log(typeof dog); // 'object'
console.log(dog.toString()); // '[object Animal.Dog]'

Browser Usage

  • install any browser if necessary (MSIE6+, Firefox, Google Chrome, Safari, Opera...)
  • create new empty text file with name "example.html":
  • open the file "example.html" in the browser to run
<!DOCTYPE HTML>
<html lang="en-US">
    <head>
        <meta charset="UTF-8" />
    </head>
    <body>
        <script src="./class.dev.js" type="text/javascript"></script> 
        <script type="text/javascript">
            var MyClass = Class({
                Constructor: function () {
                    console.log("It works!");
                }
            });
            var myInstance = new MyClass(); // "It works!
        </script>
    </body>
</html>
 

Node.js Usage

  • install node.js from nodejs.org if necessary
  • create new empty text file with name "example.js":
  • type into command line window "node example.js" to run
require('class-advanced');
var MyClass = Class({
    Constructor: function () {
        console.log("It works!");
    }
});
var myInstance = new MyClass(); // "It works!

Windows Script Host Usage

  • create new empty text file with name "example.wsf":
  • doubleclick on the file "example.wsf" to run
<job>
    <script type="JScript" src="./class.dev.js"></script> 
    <script type="JScript">
        var MyClass = Class({
            Constructor: function () {
                WScript.echo("It works!");
            }
        });
        var myInstance = new MyClass(); // "It works!
    </script> 
</job>

DEMOS

Package Sidebar

Install

npm i class-advanced

Weekly Downloads

3

Version

2.1.3

License

MIT

Last publish

Collaborators

  • tomas.flidr