class-advanced
v2.1.3
Published
Universal JS classes helper - extending, constructors, static and dynamic elements, parent methods calls, self reflection, late static binding. For all browsers, Node.js, Windows Script Host and Adobe Javascript.
Maintainers
Keywords
Readme
Javascript Class (Class.js)
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-classDOWNLOAD
- download class.min.js (for production)
- download class.dev.js (for development with JSDocs comments for IDE)
<!-- 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
- 1. Basic Class - Animal
- 2. Class Dog And Cat Extends Animal
- 3. Late Static Binding
- 4. Three Extended Classes With Static Members
- 5. Three Controller Classes And Different Behaviour In Actions
- 6. Class A, B, C And Parent Methods Calls Flows
- 7. Syntax Customization
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 />"
+ " " + "- 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); // 25. 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 = '[email protected]';
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>