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.
Downloads
6
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-class
DOWNLOAD
- 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); // 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 = '[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>