34rth-javascript-core-oo 中文文档教程
TABLE OF CONTENTS
GENERAL
用于继承的 JavaScript (node.js/browser) 库和 JavaScript 中的 mixins 与上下文的强耦合。 包括私有/公共/静态成员/函数以及 mixin 架构(从 mixin 借用功能);
包/框架是 100% 独立的,不依赖于任何其他 npm 包。 node_modules 中列出的包专用于 性能比较 比较基准性能所需的模块。
INSTALLATION
in Node.js
npm install 34rth-javascript-core-oo
in Browser
github 下载 /bin/oo.js 文件
<script type="text/javascript" src="oo.js"></script>
EXAMPLES
Classes (private/public variables/functions)
从 下面的示例显示了私有、公共函数/成员、静态函数和变量以及构造函数。 请参阅示例文件。
var earth = require('34rth-javascript-core-oo');
var my_class = earth.core.object.extend(function(_super){
this.__id__ = 'my_class'; //this is optional for debugging purpose; definition does not have to be included
this.statics = {};//define the statics object:
this.statics.my_static_function = function(){
console.log('Hooray, I\'m static');
};
this.statics.my_other_static_function = function(){
console.log('So what, so am I...');
};
this.statics.STATIC_VALUE = 'I am like a rock...';
this.member_variable = 'I am a variable and can be accessed from any instance';
var private_variable = 'I am shy';
//I am the constructor, all your base are belong to us
this.__init = function(a, b, c){
console.log('Constructing is as easy as ' + a + ' ' + b + ' ' + c);
};
this.public_function = function(){
console.log('total extrovert');
};
var private_function = function(){
console.log('not really introvert, but do not really fancy being seen everywhere, anytime...');
};
});
//testing the statics
my_class.my_static_function();
my_class.my_other_static_function();
console.log(my_class.STATIC_VALUE);
var my_instance = new my_class(1,2,3);
my_instance.public_function();
try{
my_instance.private_function();
}catch(e){
console.log('really, really, really don\'t want to be called');
}
console.log(my_instance.member_variable);
console.log(my_instance.private_variable);//undefined... they said they're shy :)
Simple class inheritance and _super
简单继承调用 _super 构造函数和方法的代码示例。 请参阅示例文件。
var earth = require('34rth-javascript-core-oo');
//definition of bicycle class - extending from the core class
var bicycle = earth.core.object.extend(function(_super){
this.__id__ = 'bicycle';//this is optional for debugging purposes and can be any random string
// the bicycle class has three public member variables
this.cadence = null;
this.gear = null;
this.speed = null;
//constructor function
this.__init = function(start_cadence, start_speed, start_gear){
this.gear = start_gear;
this.speed = start_speed;
this.cadence = start_cadence;
};
this.set_cadence = function(value){
this.cadence = value;
};
this.set_gear = function(value){
this.gear = value;
};
this.apply_brake = function(decrement){
this.speed -= decrement;
};
this.speed_up = function(increment){
this.speed += increment;
};
this.get_speed = function(){
return this.speed;
};
});
//definition of mountain_bike class, inheriting from the bicycle class
var mountain_bike = bicycle.extend(function(_super){
this.__id__ = 'bicycle.mountain_bike';//this is optional for debugging purposes and can be any random string
//the mountain_bike class adds one more public member variable
this.seat_height = null;
//constructor function
this.__init = function(start_height, start_cadence, start_speed, start_gear){
_super.__init.call(this, start_cadence, start_speed, start_gear);//call super prototype
this.seat_height = start_height;
};
this.set_height = function(value){
this.seat_height = value;
};
//we're shadowing the function speed_up of bicycle
this.speed_up = function(increment){
//on a mountainbike we're speeding up much faster
this.speed += increment*1.2;
};
//we're shadowing the function get_speed of bicycle
this.get_speed = function(){
//but we're calling the function of the parent
return _super.get_speed.call(this);
};
});
var bicycle_instance = new bicycle(1, 10, 1);
var mountain_bike_instance = new mountain_bike(90, 1, 10, 1);
console.log(bicycle_instance.speed);//prints 10 as speed of has not been modified
bicycle_instance.speed_up(15);//speeding up by 15 (e.g. m/h)
console.log(bicycle_instance.speed);//prints 25 as speed of has not been modified
console.log(typeof bicycle_instance);//returns object
console.log(bicycle_instance instanceof earth.core.object);//prints true
console.log(bicycle_instance instanceof bicycle);//prints true
console.log(bicycle_instance instanceof mountain_bike);//prints false
try{
bicycle_instance.set_height(5);
}catch(e){
console.log('Bicycle does not have a set_height function');
}
console.log(mountain_bike_instance.speed);//prints 10 as speed of has not been modified
mountain_bike_instance.speed_up(5);//speeding up by 5 (e.g. m/h)
console.log(mountain_bike_instance.speed);//prints 16 (10+5*1.2);
console.log(mountain_bike_instance.get_speed() == mountain_bike_instance.speed);//returns true as shadowed function get_speed calls parent function get_speed, returning the current speed
console.log(typeof bicycle_instance);//returns object
console.log(mountain_bike_instance instanceof earth.core.object);//prints true
console.log(mountain_bike_instance instanceof bicycle);//prints true
console.log(mountain_bike_instance instanceof mountain_bike);//prints true
Static functions
在类上定义和调用静态函数。 请参阅示例文件。
var earth = require('34rth-javascript-core-oo');
var my_static_class = earth.core.object.extend(function(_super){
this.statics = [];
this.statics.say_hello = function(name){
console.log('Hello ' + name);
};
});
my_static_class.say_hello('Peter');//prints "Hello Peter"
Static functions and inheritance
继承和调用继承的静态函数。 请参阅示例文件。
var earth = require('34rth-javascript-core-oo');
var my_parent_static_class = earth.core.object.extend(function(_super){
this.statics = [];
this.statics.say_hello = function(name){
console.log('Hello ' + name);
};
});
var my_child_static_class = my_parent_static_class.extend(function(_super){
this.statics = [];
this.statics.say_bye = function(name){
console.log('Bye ' + name);
};
});
my_child_static_class.say_hello('Marry');//prints "Hello Marry"
my_child_static_class.say_bye('Peter');//prints "Bye Peter"
my_parent_static_class.say_hello('Peter');//print "Hello Peter"
try{
my_parent_static_class.say_bye('Marry');//is not defined and throws an Exception
}catch(e){
console.log('I am not defined');
}
Init Hooks
Init hooks 允许在创建类的新实例时触发任意数量的回调函数。 初始化挂钩遍历整个原型链(即父级的初始化挂钩也会被触发)。 请参阅示例文件。
var my_class = earth.core.object.extend(function(_super){
this.__init = function(number){
console.log('Yaaay, the constructor was just called');
this.number = number;
};
});
my_class.add_init_hook(function(_super){
console.log('Class has been initialised with number ' + this.number);
});
new my_class(10);
//'Yaaay, the constructor was just called
//Class has been initialised with number 10
Init Hooks and _super
Init 钩子还允许访问 _super(总是在上下文中),因此可以调用隐藏函数。 请参阅示例文件。
var earth = require('34rth-javascript-core-oo');
var my_class = earth.core.object.extend(function(_super){
this.__init = function(number){
console.log('Yaaay, the constructor just was called');
this.number = number;
};
this.my_special_function = function(){
console.log('I am ' + this.number + ' times more special than anyone else!');
};
});
var my_child_class = my_class.extend(function(_super){
this.__init = function(number){
_super.__init.call(this, number);
};
this.my_special_function = function(){
console.log('Me toooooooo!');
};
});
my_child_class.add_init_hook(function(_super){
console.log('Class has been initialised with number ' + this.number);
_super.my_special_function.call(this);
this.my_special_function();
});
new my_child_class(10);
//Yaaay, the constructor just was called
//Class has been initialised with number 10
//I am 10 times more special than anyone else!
//Me toooooooo!
Mixins
Mixin 可用于实现可在类之间共享的功能。 由于一个类只能从另一个类(JAVA 风格)继承,mixins 提供了一种减少代码重复的方法(想想 JAVA 中的面向方面的编程)。 请参阅示例文件。
var earth = require('34rth-javascript-core-oo');
var speaker = earth.core.mixin.extend(function(_super){
this.say_something = function(){
console.log('something');
};
});
//mixins can also inherit from other mixins
var hello = earth.core.mixin.extend(function(_super){
this.say_hello = function(name){
console.log('Hello ' + name);
};
});
var bye = earth.core.mixin.extend(function(_super){
this.say_goodbye = function(name){
console.log('Bye ' + name);
};
});
var app = earth.core.object.extend(function(_super){
this.includes = [speaker, hello, bye];//array of mixins to include
});
var test = new app();
test.say_something();//Prints "Something"
test.say_hello('Marry');//prints "Hello Marry"
test.say_goodbye('Marry');//prints "Bye Marry"
Mixins and Inheritance
Mixin 也可以继承函数(包括完整的继承逻辑)。 请参阅示例文件。
var earth = require('34rth-javascript-core-oo');
var speaker = earth.core.mixin.extend(function(_super){
this.say_something = function(){
console.log('something');
};
});
//mixins can also inherit from other mixins
var speaker_with_good_memory = speaker.extend(function(_super){
this.names = [];
this.say_hello = function(name){
this.names.push(name);
console.log('Hello ' + this.names.join(', '));
};
});
var app = earth.core.object.extend(function(_super){
this.includes = [speaker, speaker_with_good_memory];//array of mixins to include
});
var test = new app();
test.say_something();//Prints "Something"
test.say_hello('Peter');//prints "Hello Peter"
test.say_hello('Marry');//prints "Hello Peter, Marry"
test.say_hello('Charly');//prints "Hello Peter, Marry, Charly"
Mixin and chaining
简单地说,如果需要,可以通过返回 this 来链接所有调用。 请参阅示例文件。
var earth = require('34rth-javascript-core-oo');
var speaker = earth.core.mixin.extend(function(_super){
this.say_something = function(){
console.log('something');
return this;//returns a reference to the object mixing
};
});
//mixins can also inherit from other mixins
var speaker_with_good_memory = speaker.extend(function(_super){
this.names = [];
this.say_hello = function(name){
this.names.push(name);
console.log('Hello ' + this.names.join(', '));
return this;//returns a reference to the object mixing
};
});
var app = earth.core.object.extend(function(_super){
this.includes = [speaker, speaker_with_good_memory];//array of mixins to include
});
var test = new app();
test.say_something().say_hello('Peter').say_hello('Marry').say_hello('Charly');
Syntactic sugar (super calls in every class)
有一个内置选项可以使语法 super 糖在整个私有/公共类函数中直接引用超级方法。 请注意,与性能比较 中的结果相比,超级 糖的性能损失大约为 40%。 下面的示例与 简单类继承和 _super 相同,只是打开了句法 super 糖。
根据下面的示例文件,通过将选项 {super:true} 传递给类定义来启用super 糖。
var earth = require('34rth-javascript-core-oo');
//definition of bicycle class - extending from the core class
var bicycle = earth.core.object.extend(function(_super){
this.__id__ = 'bicycle';//this is optional for debugging purposes and can be any random string
// the bicycle class has three public member variables
this.cadence = null;
this.gear = null;
this.speed = null;
//constructor function
this.__init = function(start_cadence, start_speed, start_gear){
this.gear = start_gear;
this.speed = start_speed;
this.cadence = start_cadence;
};
this.set_cadence = function(value){
this.cadence = value;
};
this.set_gear = function(value){
this.gear = value;
};
this.apply_brake = function(decrement){
this.speed -= decrement;
};
this.speed_up = function(increment){
this.speed += increment;
};
this.get_speed = function(){
return this.speed;
};
}, {super:true});
//definition of mountain_bike class, inheriting from the bicycle class
var mountain_bike = bicycle.extend(function(_super){
this.__id__ = 'bicycle.mountain_bike';//this is optional for debugging purposes and can be any random string
//the mountain_bike class adds one more public member variable
this.seat_height = null;
//constructor function
this.__init = function(start_height, start_cadence, start_speed, start_gear){
this.super(start_cadence, start_speed, start_gear);//call super prototype; EQUIVALENT TO: _super.__init.call(this, start_cadence, start_speed, start_gear);//call super prototype
this.seat_height = start_height;
};
this.set_height = function(value){
this.seat_height = value;
};
//we're shadowing the function speed_up of bicycle
this.speed_up = function(increment){
//on a mountainbike we're speeding up much faster
this.speed += increment*1.2;
};
//we're shadowing the function get_speed of bicycle
this.get_speed = function(){
//but we're calling the function of the parent
return this.super();//EQUIVALENT TO: return _super.get_speed.call(this);
};
}, {super:true});
var bicycle_instance = new bicycle(1, 10, 1);
var mountain_bike_instance = new mountain_bike(90, 1, 10, 1);
console.log(bicycle_instance.speed);//prints 10 as speed of has not been modified
bicycle_instance.speed_up(15);//speeding up by 15 (e.g. m/h)
console.log(bicycle_instance.speed);//prints 25 as speed of has not been modified
console.log(typeof bicycle_instance);//returns object
console.log(bicycle_instance instanceof earth.core.object);//prints true
console.log(bicycle_instance instanceof bicycle);//prints true
console.log(bicycle_instance instanceof mountain_bike);//prints false
try{
bicycle_instance.set_height(5);
}catch(e){
console.log('Bicycle does not have a set_height function');
}
console.log(mountain_bike_instance.speed);//prints 10 as speed of has not been modified
mountain_bike_instance.speed_up(5);//speeding up by 5 (e.g. m/h)
console.log(mountain_bike_instance.speed);//prints 16 (10+5*1.2);
console.log(mountain_bike_instance.get_speed() == mountain_bike_instance.speed);//returns true as shadowed function get_speed calls parent function get_speed, returning the current speed
console.log(typeof bicycle_instance);//returns object
console.log(mountain_bike_instance instanceof earth.core.object);//prints true
console.log(mountain_bike_instance instanceof bicycle);//prints true
console.log(mountain_bike_instance instanceof mountain_bike);//prints true
PERFORMANCE COMPARISON
与以下库/脚本的性能比较:
- inherit
- Lava Class Manager
- TypeScript
- Fiber
- John Resig's extend function
- Augment
- jsface
- Native JavaScript inheritance
要运行性能测试,请运行
node performance/run_proprietary.js
有两个可用参数:
- --benchmark/-b: the type of benchmark to run. Valid values: instantiation, public, static
- --class/-c: the class that should be benchmarked.
Overall performance results
| # | 图书馆 | 操作/毫秒 | 总时间(以毫秒为单位) | 总样本量 | | :-: | --- | --: | --: | --: | | 1 | 第 34 位 | 13,355 | 4,151 | 40,000,000 | | 2 | 增加 | 12,663 | 4,372 | 40,000,000 | | 3 | 本机 | 10,905 | 4,417 | 40,000,000 | | 4 | Lava.ClassManager 单态 | 9,438 | 4,564 | 40,000,000 | | 5 | 打字稿 | 9,377 | 4,657 | 40,000,000 | | 6 | 页面 | 10,140 | 4,965 | 40,000,000 | | 7 | Lava.ClassManager 多态 | 8,958 | 5,348 | 个 40,000,000 | | 8 | 继承 | 8,600 | 5,738 | 40,000,000 | | 9 | 纤维 | 7,581 | 6,028 | 40,000,000 | | 10 | John Resig 的课程 | 3,779 | 13,194 | 40,000,000 |
Specific performance per example
Instantiation (inheritance depth 1)
| # | 图书馆 | 操作/毫秒 | 总时间(以毫秒为单位) | 样本量 | | :-: | --- | --: | --: | --: | | 1 | 增加 | 20,734 | 482.31 | 10,000,000 | | 2 | 第 34 位 | 20,343 | 491.58 | 10,000,000 | | 3 | 本机 | 16,965 | 589.45 | 10,000,000 | | 4 | 页面 | 16,937 | 590.42 | 10,000,000 | | 5 | 继承 | 13,381 | 747.34 | 10,000,000 | | 6 | 打字稿 | 13,336 | 个 749.87 | 10,000,000 | | 7 | Lava.ClassManager 多态 | 12,748 | 个 784.42 | 10,000,000 | | 8 | Lava.ClassManager 单态 | 12,093 | 826.93 | 10,000,000 | | 9 | 纤维 | 11,161 | 895.98 | 10,000,000 | | 10 | John Resig 的课程 | 6,744 | 1,482.78 | 10,000,000 |
Instantiation (inheritance depth 2)
| # | 图书馆 | 操作/毫秒 | 总时间(以毫秒为单位) | 样本量 | | :-: | --- | --: | --: | --: | | 1 | 第 34 位 | 20,392 | 490.39 | 10,000,000 | | 2 | 增加 | 17,876 | 559.42 | 10,000,000 | | 3 | 本机 | 13,636 | 733.37 | 10,000,000 | | 4 | Lava.ClassManager 多态 | 12,426 | 个 804.77 | 10,000,000 | | 5 | 页面 | 12,332 | 810.87 | 10,000,000 | | 6 | Lava.ClassManager 单态 | 11,824 | 845.74 | 10,000,000 | | 7 | 继承 | 11,194 | 893.31 | 10,000,000 | | 8 | 打字稿 | 10,630 | 940.72 | 10,000,000 | | 9 | 纤维 | 9,210 | 1,085.78 | 10,000,000 | | 10 | John Resig 的课程 | 3,889 | 2,571.49 | 10,000,000 |
Public Method Invocation (inheritance depth 1)
| # | 图书馆 | 操作/毫秒 | 总时间(以毫秒为单位) | 样本量 | | :-: | --- | --: | --: | --: | | 1 | 打字稿 | 7,215 | 1,385.91 | 10,000,000 | | 2 | 本机 | 7,049 | 1,418.74 | 10,000,000 | | 3 | Lava.ClassManager 单态 | 6,959 | 1,436.98 | 10,000,000 | | 4 | 第 34 位 | 6,786 | 1,473.63 | 10,000,000 | | 5 | 增加 | 6,316 | 1,583.35 | 10,000,000 | | 6 | 页面 | 6,088 | 1,642.56 | 10,000,000 | | 7 | Lava.ClassManager 多态 | 5,530 | 1,808.43 | 10,000,000 | | 8 | 纤维 | 5,381 | 1,858.52 | 10,000,000 | | 9 | 继承 | 5,307 | 1,884.27 | 10,000,000 | | 10 | John Resig 的课程 | 2,581 | 3,875.01 | 10,000,000 |
Public Method Invocation (inheritance depth 2)
| # | 图书馆 | 操作/毫秒 | 总时间(以毫秒为单位) | 样本量 | | :-: | --- | --: | --: | --: | | 1 | Lava.ClassManager 单态 | 6,876 | 1,454.40 | 10,000,000 | | 2 | 打字稿 | 6,325 | 1,580.93 | 10,000,000 | | 3 | 本机 | 5,968 | 1,675.62 | 10,000,000 | | 4 | 第 34 位 | 5,899 | 1,695.13 | 10,000,000 | | 5 | 增加 | 5,725 | 1,746.59 | 10,000,000 | | 6 | 页面 | 5,204 | 1,921.58 | 10,000,000 | | 7 | Lava.ClassManager 多态 | 5,126 | 个 1,950.87 | 10,000,000 | | 8 | 纤维 | 4,570 | 2,188.11 | 10,000,000 | | 9 | 继承 | 4,519 | 2,213.08 | 10,000,000 | | 10 | John Resig 的课程 | 1,900 | 5,264.32 | 10,000,000 |
Static Method Invocation
| # | 图书馆 | 操作/毫秒 | 总时间(以毫秒为单位) | 样本量 | | :-: | --- | --: | --: | --: | | 1 | 第 34 位 | 14,559 | 686.88 | 10,000,000 | | 2 | 打字稿 | 13,608 | 734.84 | 10,000,000 | | 3 | 页面 | 12,938 | 772.92 | 10,000,000 | | 4 | 继承 | 12,279 | 814.38 | 10,000,000 | | 5 | 本机 | 不\一个 | 不\一个 | 不\一个 | | 6 | John Resig 的课程 | 不\一个 | 不\一个 | 不\一个 | | 7 | 纤维 | 不\一个 | 不\一个 | 不\一个 | | 8 | Lava.ClassManager 多态 | 不\一个 | 不\一个 | 不\一个 | | 9 | Lava.ClassManager 单态 | 不\一个 | 不\一个 | 不\一个 | | 10| 增加 | 不\一个 | 不\一个 | 不\一个 |
TESTS
有关 mocha 测试结果,请参阅 Travis CI。
node make.js && ./node_modules/mocha/bin/mocha
TABLE OF CONTENTS
GENERAL
JavaScript (node.js/browser) library for inheritance and mixins in JavaScript with strong coupling of contexts. Includes private/public/static members/functions as well as mixin architecture (borrowing functionality from mixins);
The package/framework is 100% stand-alone and is not dependant on any other npm packages. The packages listed in node_modules are exclusively for the performance comparison modules required to compare the baseline performances.
INSTALLATION
in Node.js
npm install 34rth-javascript-core-oo
in Browser
Download the /bin/oo.js file from github
<script type="text/javascript" src="oo.js"></script>
EXAMPLES
Classes (private/public variables/functions)
The example below shows the private, public functions/members, static functions and variables as well as the constructor. See example file.
var earth = require('34rth-javascript-core-oo');
var my_class = earth.core.object.extend(function(_super){
this.__id__ = 'my_class'; //this is optional for debugging purpose; definition does not have to be included
this.statics = {};//define the statics object:
this.statics.my_static_function = function(){
console.log('Hooray, I\'m static');
};
this.statics.my_other_static_function = function(){
console.log('So what, so am I...');
};
this.statics.STATIC_VALUE = 'I am like a rock...';
this.member_variable = 'I am a variable and can be accessed from any instance';
var private_variable = 'I am shy';
//I am the constructor, all your base are belong to us
this.__init = function(a, b, c){
console.log('Constructing is as easy as ' + a + ' ' + b + ' ' + c);
};
this.public_function = function(){
console.log('total extrovert');
};
var private_function = function(){
console.log('not really introvert, but do not really fancy being seen everywhere, anytime...');
};
});
//testing the statics
my_class.my_static_function();
my_class.my_other_static_function();
console.log(my_class.STATIC_VALUE);
var my_instance = new my_class(1,2,3);
my_instance.public_function();
try{
my_instance.private_function();
}catch(e){
console.log('really, really, really don\'t want to be called');
}
console.log(my_instance.member_variable);
console.log(my_instance.private_variable);//undefined... they said they're shy :)
Simple class inheritance and _super
Code example for simple inheritance calling _super constructor and methods. See example file.
var earth = require('34rth-javascript-core-oo');
//definition of bicycle class - extending from the core class
var bicycle = earth.core.object.extend(function(_super){
this.__id__ = 'bicycle';//this is optional for debugging purposes and can be any random string
// the bicycle class has three public member variables
this.cadence = null;
this.gear = null;
this.speed = null;
//constructor function
this.__init = function(start_cadence, start_speed, start_gear){
this.gear = start_gear;
this.speed = start_speed;
this.cadence = start_cadence;
};
this.set_cadence = function(value){
this.cadence = value;
};
this.set_gear = function(value){
this.gear = value;
};
this.apply_brake = function(decrement){
this.speed -= decrement;
};
this.speed_up = function(increment){
this.speed += increment;
};
this.get_speed = function(){
return this.speed;
};
});
//definition of mountain_bike class, inheriting from the bicycle class
var mountain_bike = bicycle.extend(function(_super){
this.__id__ = 'bicycle.mountain_bike';//this is optional for debugging purposes and can be any random string
//the mountain_bike class adds one more public member variable
this.seat_height = null;
//constructor function
this.__init = function(start_height, start_cadence, start_speed, start_gear){
_super.__init.call(this, start_cadence, start_speed, start_gear);//call super prototype
this.seat_height = start_height;
};
this.set_height = function(value){
this.seat_height = value;
};
//we're shadowing the function speed_up of bicycle
this.speed_up = function(increment){
//on a mountainbike we're speeding up much faster
this.speed += increment*1.2;
};
//we're shadowing the function get_speed of bicycle
this.get_speed = function(){
//but we're calling the function of the parent
return _super.get_speed.call(this);
};
});
var bicycle_instance = new bicycle(1, 10, 1);
var mountain_bike_instance = new mountain_bike(90, 1, 10, 1);
console.log(bicycle_instance.speed);//prints 10 as speed of has not been modified
bicycle_instance.speed_up(15);//speeding up by 15 (e.g. m/h)
console.log(bicycle_instance.speed);//prints 25 as speed of has not been modified
console.log(typeof bicycle_instance);//returns object
console.log(bicycle_instance instanceof earth.core.object);//prints true
console.log(bicycle_instance instanceof bicycle);//prints true
console.log(bicycle_instance instanceof mountain_bike);//prints false
try{
bicycle_instance.set_height(5);
}catch(e){
console.log('Bicycle does not have a set_height function');
}
console.log(mountain_bike_instance.speed);//prints 10 as speed of has not been modified
mountain_bike_instance.speed_up(5);//speeding up by 5 (e.g. m/h)
console.log(mountain_bike_instance.speed);//prints 16 (10+5*1.2);
console.log(mountain_bike_instance.get_speed() == mountain_bike_instance.speed);//returns true as shadowed function get_speed calls parent function get_speed, returning the current speed
console.log(typeof bicycle_instance);//returns object
console.log(mountain_bike_instance instanceof earth.core.object);//prints true
console.log(mountain_bike_instance instanceof bicycle);//prints true
console.log(mountain_bike_instance instanceof mountain_bike);//prints true
Static functions
Defining and calling static functions on classes. See example file.
var earth = require('34rth-javascript-core-oo');
var my_static_class = earth.core.object.extend(function(_super){
this.statics = [];
this.statics.say_hello = function(name){
console.log('Hello ' + name);
};
});
my_static_class.say_hello('Peter');//prints "Hello Peter"
Static functions and inheritance
Inheriting and calling inherited static functions. See example file.
var earth = require('34rth-javascript-core-oo');
var my_parent_static_class = earth.core.object.extend(function(_super){
this.statics = [];
this.statics.say_hello = function(name){
console.log('Hello ' + name);
};
});
var my_child_static_class = my_parent_static_class.extend(function(_super){
this.statics = [];
this.statics.say_bye = function(name){
console.log('Bye ' + name);
};
});
my_child_static_class.say_hello('Marry');//prints "Hello Marry"
my_child_static_class.say_bye('Peter');//prints "Bye Peter"
my_parent_static_class.say_hello('Peter');//print "Hello Peter"
try{
my_parent_static_class.say_bye('Marry');//is not defined and throws an Exception
}catch(e){
console.log('I am not defined');
}
Init Hooks
Init hooks allow to trigger any number of callback functions whenever a new instance of a class is created. Init hooks traverse the whole prototype chain (ie parent's init hooks will also get triggered). See example file.
var my_class = earth.core.object.extend(function(_super){
this.__init = function(number){
console.log('Yaaay, the constructor was just called');
this.number = number;
};
});
my_class.add_init_hook(function(_super){
console.log('Class has been initialised with number ' + this.number);
});
new my_class(10);
//'Yaaay, the constructor was just called
//Class has been initialised with number 10
Init Hooks and _super
Init hooks also allow access to _super (always in context) so shadowed functions can be called. See example file.
var earth = require('34rth-javascript-core-oo');
var my_class = earth.core.object.extend(function(_super){
this.__init = function(number){
console.log('Yaaay, the constructor just was called');
this.number = number;
};
this.my_special_function = function(){
console.log('I am ' + this.number + ' times more special than anyone else!');
};
});
var my_child_class = my_class.extend(function(_super){
this.__init = function(number){
_super.__init.call(this, number);
};
this.my_special_function = function(){
console.log('Me toooooooo!');
};
});
my_child_class.add_init_hook(function(_super){
console.log('Class has been initialised with number ' + this.number);
_super.my_special_function.call(this);
this.my_special_function();
});
new my_child_class(10);
//Yaaay, the constructor just was called
//Class has been initialised with number 10
//I am 10 times more special than anyone else!
//Me toooooooo!
Mixins
Mixins can be used to implement functionality that can be shared between classes. As a class can only inherit from one other class (JAVA-style) mixins allow a way to reduce duplication of code (think aspect oriented programming in JAVA). See example file.
var earth = require('34rth-javascript-core-oo');
var speaker = earth.core.mixin.extend(function(_super){
this.say_something = function(){
console.log('something');
};
});
//mixins can also inherit from other mixins
var hello = earth.core.mixin.extend(function(_super){
this.say_hello = function(name){
console.log('Hello ' + name);
};
});
var bye = earth.core.mixin.extend(function(_super){
this.say_goodbye = function(name){
console.log('Bye ' + name);
};
});
var app = earth.core.object.extend(function(_super){
this.includes = [speaker, hello, bye];//array of mixins to include
});
var test = new app();
test.say_something();//Prints "Something"
test.say_hello('Marry');//prints "Hello Marry"
test.say_goodbye('Marry');//prints "Bye Marry"
Mixins and Inheritance
Mixins can also inherit functions (including full inheritance logic). See example file.
var earth = require('34rth-javascript-core-oo');
var speaker = earth.core.mixin.extend(function(_super){
this.say_something = function(){
console.log('something');
};
});
//mixins can also inherit from other mixins
var speaker_with_good_memory = speaker.extend(function(_super){
this.names = [];
this.say_hello = function(name){
this.names.push(name);
console.log('Hello ' + this.names.join(', '));
};
});
var app = earth.core.object.extend(function(_super){
this.includes = [speaker, speaker_with_good_memory];//array of mixins to include
});
var test = new app();
test.say_something();//Prints "Something"
test.say_hello('Peter');//prints "Hello Peter"
test.say_hello('Marry');//prints "Hello Peter, Marry"
test.say_hello('Charly');//prints "Hello Peter, Marry, Charly"
Mixin and chaining
Trivially, all calls can be chained if desired, by returning this. See example file.
var earth = require('34rth-javascript-core-oo');
var speaker = earth.core.mixin.extend(function(_super){
this.say_something = function(){
console.log('something');
return this;//returns a reference to the object mixing
};
});
//mixins can also inherit from other mixins
var speaker_with_good_memory = speaker.extend(function(_super){
this.names = [];
this.say_hello = function(name){
this.names.push(name);
console.log('Hello ' + this.names.join(', '));
return this;//returns a reference to the object mixing
};
});
var app = earth.core.object.extend(function(_super){
this.includes = [speaker, speaker_with_good_memory];//array of mixins to include
});
var test = new app();
test.say_something().say_hello('Peter').say_hello('Marry').say_hello('Charly');
Syntactic sugar (super calls in every class)
There is an in-built option to enable syntactic super sugar to have direct reference to super methods throughout the private/public class functions. Please note that the super sugar comes at a performance penalty of roughtly 40% compared to the results in the PERFORMANCE COMPARISON. The example below is the same as in Simple class inheritance and _super just with syntactic super sugar turned on.
Enabling the super sugar is done by passing the option {super:true} to the class definition as per the example file below.
var earth = require('34rth-javascript-core-oo');
//definition of bicycle class - extending from the core class
var bicycle = earth.core.object.extend(function(_super){
this.__id__ = 'bicycle';//this is optional for debugging purposes and can be any random string
// the bicycle class has three public member variables
this.cadence = null;
this.gear = null;
this.speed = null;
//constructor function
this.__init = function(start_cadence, start_speed, start_gear){
this.gear = start_gear;
this.speed = start_speed;
this.cadence = start_cadence;
};
this.set_cadence = function(value){
this.cadence = value;
};
this.set_gear = function(value){
this.gear = value;
};
this.apply_brake = function(decrement){
this.speed -= decrement;
};
this.speed_up = function(increment){
this.speed += increment;
};
this.get_speed = function(){
return this.speed;
};
}, {super:true});
//definition of mountain_bike class, inheriting from the bicycle class
var mountain_bike = bicycle.extend(function(_super){
this.__id__ = 'bicycle.mountain_bike';//this is optional for debugging purposes and can be any random string
//the mountain_bike class adds one more public member variable
this.seat_height = null;
//constructor function
this.__init = function(start_height, start_cadence, start_speed, start_gear){
this.super(start_cadence, start_speed, start_gear);//call super prototype; EQUIVALENT TO: _super.__init.call(this, start_cadence, start_speed, start_gear);//call super prototype
this.seat_height = start_height;
};
this.set_height = function(value){
this.seat_height = value;
};
//we're shadowing the function speed_up of bicycle
this.speed_up = function(increment){
//on a mountainbike we're speeding up much faster
this.speed += increment*1.2;
};
//we're shadowing the function get_speed of bicycle
this.get_speed = function(){
//but we're calling the function of the parent
return this.super();//EQUIVALENT TO: return _super.get_speed.call(this);
};
}, {super:true});
var bicycle_instance = new bicycle(1, 10, 1);
var mountain_bike_instance = new mountain_bike(90, 1, 10, 1);
console.log(bicycle_instance.speed);//prints 10 as speed of has not been modified
bicycle_instance.speed_up(15);//speeding up by 15 (e.g. m/h)
console.log(bicycle_instance.speed);//prints 25 as speed of has not been modified
console.log(typeof bicycle_instance);//returns object
console.log(bicycle_instance instanceof earth.core.object);//prints true
console.log(bicycle_instance instanceof bicycle);//prints true
console.log(bicycle_instance instanceof mountain_bike);//prints false
try{
bicycle_instance.set_height(5);
}catch(e){
console.log('Bicycle does not have a set_height function');
}
console.log(mountain_bike_instance.speed);//prints 10 as speed of has not been modified
mountain_bike_instance.speed_up(5);//speeding up by 5 (e.g. m/h)
console.log(mountain_bike_instance.speed);//prints 16 (10+5*1.2);
console.log(mountain_bike_instance.get_speed() == mountain_bike_instance.speed);//returns true as shadowed function get_speed calls parent function get_speed, returning the current speed
console.log(typeof bicycle_instance);//returns object
console.log(mountain_bike_instance instanceof earth.core.object);//prints true
console.log(mountain_bike_instance instanceof bicycle);//prints true
console.log(mountain_bike_instance instanceof mountain_bike);//prints true
PERFORMANCE COMPARISON
Performance comparison against the following libraries/scripts:
- inherit
- Lava Class Manager
- TypeScript
- Fiber
- John Resig's extend function
- Augment
- jsface
- Native JavaScript inheritance
To run the performance tests, run
node performance/run_proprietary.js
There are two parameters available:
- --benchmark/-b: the type of benchmark to run. Valid values: instantiation, public, static
- --class/-c: the class that should be benchmarked.
Overall performance results
| # | Library | ops/ms | total time (in ms) | total sample size | | :-: | --- | --: | --: | --: | | 1 | 34rth | 13,355 | 4,151 | 40,000,000 | | 2 | augment | 12,663 | 4,372 | 40,000,000 | | 3 | native | 10,905 | 4,417 | 40,000,000 | | 4 | Lava.ClassManager monomorphic | 9,438 | 4,564 | 40,000,000 | | 5 | Typescript | 9,377 | 4,657 | 40,000,000 | | 6 | jsface | 10,140 | 4,965 | 40,000,000 | | 7 | Lava.ClassManager polymorphic | 8,958 | 5,348 | 40,000,000 | | 8 | inherits | 8,600 | 5,738 | 40,000,000 | | 9 | Fiber | 7,581 | 6,028 | 40,000,000 | | 10 | John Resig’s Class | 3,779 | 13,194 | 40,000,000 |
Specific performance per example
Instantiation (inheritance depth 1)
| # | Library | ops/ms | total time (in ms) | Sample Size | | :-: | --- | --: | --: | --: | | 1 | augment | 20,734 | 482.31 | 10,000,000 | | 2 | 34rth | 20,343 | 491.58 | 10,000,000 | | 3 | native | 16,965 | 589.45 | 10,000,000 | | 4 | jsface | 16,937 | 590.42 | 10,000,000 | | 5 | inherits | 13,381 | 747.34 | 10,000,000 | | 6 | Typescript | 13,336 | 749.87 | 10,000,000 | | 7 | Lava.ClassManager polymorphic | 12,748 | 784.42 | 10,000,000 | | 8 | Lava.ClassManager monomorphic | 12,093 | 826.93 | 10,000,000 | | 9 | Fiber | 11,161 | 895.98 | 10,000,000 | | 10 | John Resig's Class | 6,744 | 1,482.78 | 10,000,000 |
Instantiation (inheritance depth 2)
| # | Library | ops/ms | total time (in ms) | Sample Size | | :-: | --- | --: | --: | --: | | 1 | 34rth | 20,392 | 490.39 | 10,000,000 | | 2 | augment | 17,876 | 559.42 | 10,000,000 | | 3 | native | 13,636 | 733.37 | 10,000,000 | | 4 | Lava.ClassManager polymorphic | 12,426 | 804.77 | 10,000,000 | | 5 | jsface | 12,332 | 810.87 | 10,000,000 | | 6 | Lava.ClassManager monomorphic | 11,824 | 845.74 | 10,000,000 | | 7 | inherits | 11,194 | 893.31 | 10,000,000 | | 8 | Typescript | 10,630 | 940.72 | 10,000,000 | | 9 | Fiber | 9,210 | 1,085.78 | 10,000,000 | | 10 | John Resig's Class | 3,889 | 2,571.49 | 10,000,000 |
Public Method Invocation (inheritance depth 1)
| # | Library | ops/ms | total time (in ms) | Sample Size | | :-: | --- | --: | --: | --: | | 1 | Typescript | 7,215 | 1,385.91 | 10,000,000 | | 2 | native | 7,049 | 1,418.74 | 10,000,000 | | 3 | Lava.ClassManager monomorphic | 6,959 | 1,436.98 | 10,000,000 | | 4 | 34rth | 6,786 | 1,473.63 | 10,000,000 | | 5 | augment | 6,316 | 1,583.35 | 10,000,000 | | 6 | jsface | 6,088 | 1,642.56 | 10,000,000 | | 7 | Lava.ClassManager polymorphic | 5,530 | 1,808.43 | 10,000,000 | | 8 | Fiber | 5,381 | 1,858.52 | 10,000,000 | | 9 | inherits | 5,307 | 1,884.27 | 10,000,000 | | 10 | John Resig's Class | 2,581 | 3,875.01 | 10,000,000 |
Public Method Invocation (inheritance depth 2)
| # | Library | ops/ms | total time (in ms) | Sample Size | | :-: | --- | --: | --: | --: | | 1 | Lava.ClassManager monomorphic | 6,876 | 1,454.40 | 10,000,000 | | 2 | Typescript | 6,325 | 1,580.93 | 10,000,000 | | 3 | native | 5,968 | 1,675.62 | 10,000,000 | | 4 | 34rth | 5,899 | 1,695.13 | 10,000,000 | | 5 | augment | 5,725 | 1,746.59 | 10,000,000 | | 6 | jsface | 5,204 | 1,921.58 | 10,000,000 | | 7 | Lava.ClassManager polymorphic | 5,126 | 1,950.87 | 10,000,000 | | 8 | Fiber | 4,570 | 2,188.11 | 10,000,000 | | 9 | inherits | 4,519 | 2,213.08 | 10,000,000 | | 10 | John Resig's Class | 1,900 | 5,264.32 | 10,000,000 |
Static Method Invocation
| # | Library | ops/ms | total time (in ms) | Sample Size | | :-: | --- | --: | --: | --: | | 1 | 34rth | 14,559 | 686.88 | 10,000,000 | | 2 | Typescript | 13,608 | 734.84 | 10,000,000 | | 3 | jsface | 12,938 | 772.92 | 10,000,000 | | 4 | inherits | 12,279 | 814.38 | 10,000,000 | | 5 | native | n\a | n\a | n\a | | 6 | John Resig's Class | n\a | n\a | n\a | | 7 | Fiber | n\a | n\a | n\a | | 8 | Lava.ClassManager polymorphic | n\a | n\a | n\a | | 9 | Lava.ClassManager monomorphic | n\a | n\a | n\a | | 10| augment | n\a | n\a | n\a |
TESTS
For mocha test results see Travis CI.
node make.js && ./node_modules/mocha/bin/mocha