JavaScript OOP in NodeJS: how?
I am used to the classical OOP as in Java.
What are the best practices to do OOP in JavaScript using NodeJS?
Each Class is a file with module.export
?
How to create Classes?
this.Class = function() {
//constructor?
var privateField = ""
this.publicField = ""
var privateMethod = function() {}
this.publicMethod = function() {}
}
vs. (I am not even sure it is correct)
this.Class = {
privateField: ""
, privateMethod: function() {}
, return {
publicField: ""
publicMethod: function() {}
}
}
vs.
this.Class = function() {}
this.Class.prototype.method = function(){}
...
How would inheritance work?
Are there specific modules for implementing OOP in NodeJS?
I am finding a thousand different ways to create things that resemble OOP.. but I have no clue what is the most used/practical/clean way.
Bonus question: what is the suggested "OOP style" to use with MongooseJS? (can a MongooseJS document be seen as a Class and a model used as an instance?)
EDIT
here is an example in JsFiddle please provide feedback.
//http://javascriptissexy.com/oop-in-javascript-what-you-need-to-know/
function inheritPrototype(childObject, parentObject) {
var copyOfParent = Object.create(parentObject.prototype)
copyOfParent.constructor = childObject
childObject.prototype = copyOfParent
}
//example
function Canvas (id) {
this.id = id
this.shapes = {} //instead of array?
console.log("Canvas constructor called "+id)
}
Canvas.prototype = {
constructor: Canvas
, getId: function() {
return this.id
}
, getShape: function(shapeId) {
return this.shapes[shapeId]
}
, getShapes: function() {
return this.shapes
}
, addShape: function (shape) {
this.shapes[shape.getId()] = shape
}
, removeShape: function (shapeId) {
var shape = this.shapes[shapeId]
if (shape)
delete this.shapes[shapeId]
return shape
}
}
function Shape(id) {
this.id = id
this.size = { width: 0, height: 0 }
console.log("Shape constructor called "+id)
}
Shape.prototype = {
constructor: Shape
, getId: function() {
return this.id
}
, getSize: function() {
return this.size
}
, setSize: function (size) {
this.size = size
}
}
//inheritance
function Square(id, otherSuff) {
Shape.call(this, id) //same as Shape.prototype.constructor.apply( this, arguments ); ?
this.stuff = otherSuff
console.log("Square constructor called "+id)
}
inheritPrototype(Square, Shape)
Square.prototype.getSize = function() { //override
return this.size.width
}
function ComplexShape(id) {
Shape.call(this, id)
this.frame = null
console.log("ComplexShape constructor called "+id)
}
inheritPrototype(ComplexShape, Shape)
ComplexShape.prototype.getFrame = function() {
return this.frame
}
ComplexShape.prototype.setFrame = function(frame) {
this.frame = frame
}
function Frame(id) {
this.id = id
this.length = 0
}
Frame.prototype = {
constructor: Frame
, getId: function() {
return this.id
}
, getLength: function() {
return this.length
}
, setLength: function (length) {
this.length = length
}
}
/////run
var aCanvas = new Canvas("c1")
var anotherCanvas = new Canvas("c2")
console.log("aCanvas: "+ aCanvas.getId())
var aSquare = new Square("s1", {})
aSquare.setSize({ width: 100, height: 100})
console.log("square overridden size: "+aSquare.getSize())
var aComplexShape = new ComplexShape("supercomplex")
var aFrame = new Frame("f1")
aComplexShape.setFrame(aFrame)
console.log(aComplexShape.getFrame())
aCanvas.addShape(aSquare)
aCanvas.addShape(aComplexShape)
console.log("Shapes in aCanvas: "+Object.keys(aCanvas.getShapes()).length)
anotherCanvas.addShape(aCanvas.removeShape("supercomplex"))
console.log("Shapes in aCanvas: "+Object.keys(aCanvas.getShapes()).length)
console.log("Shapes in anotherCanvas: "+Object.keys(anotherCanvas.getShapes()).length)
console.log(aSquare instanceof Shape)
console.log(aComplexShape instanceof Shape)
This is an example that works out of the box. If you want less "hacky", you should use inheritance library or such.
Well in a file animal.js you would write:
var method = Animal.prototype;
function Animal(age) {
this._age = age;
}
method.getAge = function() {
return this._age;
};
module.exports = Animal;
To use it in other file:
var Animal = require("./animal.js");
var john = new Animal(3);
If you want a "sub class" then inside mouse.js:
var _super = require("./animal.js").prototype,
method = Mouse.prototype = Object.create( _super );
method.constructor = Mouse;
function Mouse() {
_super.constructor.apply( this, arguments );
}
//Pointless override to show super calls
//note that for performance (e.g. inlining the below is impossible)
//you should do
//method.$getAge = _super.getAge;
//and then use this.$getAge() instead of super()
method.getAge = function() {
return _super.getAge.call(this);
};
module.exports = Mouse;
Also you can consider "Method borrowing" instead of vertical inheritance. You don't need to inherit from a "class" to use its method on your class. For instance:
var method = List.prototype;
function List() {
}
method.add = Array.prototype.push;
...
var a = new List();
a.add(3);
console.log(a[0]) //3;
As Node.js community ensure new features from the JavaScript ECMA-262 specification are brought to Node.js developers in a timely manner.
You can take a look at JavaScript classes. MDN link to JS classes In the ECMAScript 6 JavaScript classes are introduced, this method provide easier way to model OOP concepts in Javascript.
Note : JS classes will work in only strict mode.
Below is some skeleton of class,inheritance written in Node.js ( Used Node.js Version v5.0.0 )
Class declarations :
'use strict';
class Animal{
constructor(name){
this.name = name ;
}
print(){
console.log('Name is :'+ this.name);
}
}
var a1 = new Animal('Dog');
Inheritance :
'use strict';
class Base{
constructor(){
}
// methods definitions go here
}
class Child extends Base{
// methods definitions go here
print(){
}
}
var childObj = new Child();
I suggest to use the inherits
helper that comes with the standard util
module: http://nodejs.org/api/util.html#util_util_inherits_constructor_superconstructor
There is an example of how to use it on the linked page.
This is the best video about Object-Oriented JavaScript on the internet:
The Definitive Guide to Object-Oriented JavaScript
Watch from beginning to end!!
Basically, Javascript is a Prototype-based language which is quite different than the classes in Java, C++, C#, and other popular friends. The video explains the core concepts far better than any answer here.
With ES6 (released 2015) we got a "class" keyword which allows us to use Javascript "classes" like we would with Java, C++, C#, Swift, etc.
Screenshot from the video showing how to write and instantiate a Javascript class/subclass:
In the Javascript community, lots of people argue that OOP should not be used because the prototype model does not allow to do a strict and robust OOP natively. However, I don't think that OOP is a matter of langage but rather a matter of architecture.
If you want to use a real strong OOP in Javascript/Node, you can have a look at the full-stack open source framework Danf. It provides all needed features for a strong OOP code (classes, interfaces, inheritance, dependency-injection, ...). It also allows you to use the same classes on both the server (node) and client (browser) sides. Moreover, you can code your own danf modules and share them with anybody thanks to Npm.