How can I add OOP to these JS functions? - javascript

I know this may seem like a repeated question, but I am currently stuck as to the best way to approach this, limited mostly by my lack of knowledge. Thus I am here to learn.
I am trying to do some simple OOP with JavaScript but coming from C# I am having a few issues with how to best solve this problem. Below I have four "Classes"; DisplayEngine, DisplayElement, Box, and Grid.
I would like Box and Grid to inherit DisplayElement, and be able to call the base functions in each of their respective functions. Almost like super.call() or something.
How would you best approach this?
var DisplayEngine = function() {
this.elements = [];
this.add = function(element) {
this.elements.push(element);
};
this.update = function() {
this.elements.forEach(function(element) {
element.update();
})
};
this.draw = function() {
this.elements.forEach(function(element) {
element.draw();
})
};
};
var DisplayElement = function() {
this.update = function() {
console.log('DisplayElement update');
};
this.draw = function() {
console.log('DisplayElement draw');
};
};
var Box = function() {
this.update = function() {
console.log('Box update');
// call DisplayElement.update()
};
this.draw = function() {
console.log('Box draw');
// call DisplayElement.draw()
};
};
var Grid = function() {
this.update = function() {
console.log('Grid update');
// call DisplayElement.update()
};
this.draw = function() {
console.log('Grid draw');
// call DisplayElement.draw()
};
};
$(function() {
var displayEngine = new DisplayEngine();
var box = new Box();
var grid = new Grid();
displayEngine.add(box);
displayEngine.add(grid);
displayEngine.update();
displayEngine.draw();
});

Here is a way to do it with prototype, each "class" need to be in his own file, the important part is Grid.prototype = new DisplayElement(); This allow you to call function from the DisplayElement in Grid:
DisplayEngine.js
function DisplayEngine() {
this.elements = [];
}
DisplayEngine.prototype.add = function(element) {
this.elements.push(element);
}
DisplayEngine.prototype.update = function() {
this.elements.forEach(function(element) {
element.update();
})
}
DisplayEngine.prototype.draw = function() {
this.elements.forEach(function(element) {
element.draw();
})
}
DisplayElement.js
function DisplayElement() {
}
DisplayElement.prototype.updateElement = function() {
console.log('DisplayElement update');
}
DisplayElement.prototype.drawElement = function() {
console.log('DisplayElement draw');
}
Box.js
function Box() {
}
Box.prototype = new DisplayElement();
Box.prototype.update = function() {
console.log('Box update');
this.updateElement();
}
Box.prototype.draw = function() {
console.log('Box draw');
this.drawElement();
}
Grid.js
function Grid() {
}
Grid.prototype = new DisplayElement();
Box.prototype.update = function() {
console.log('Grid update');
this.updateElement();
}
Box.prototype.draw = function() {
console.log('Grid draw');
this.drawElement();
}
Main.js
$(function() {
var displayEngine = new DisplayEngine();
var box = new Box();
var grid = new Grid();
displayEngine.add(box);
displayEngine.add(grid);
displayEngine.update();
displayEngine.draw();
});

To just answer to your question, declare your objects such as :
function DisplayElement() {};
DisplayElement.prototype.update = function() {
console.log('DisplayElement update');
};
DisplayElement.prototype.draw = function() {
console.log('DisplayElement draw');
};
// ...
// Now, instanciation :
var myElement = new DisplayElement();
Then, for inheritance :
function Box() {
DisplayEngine.call(this, arguments); // Call the super constructor
}
Box.prototype = Object.create(DisplayEngine.prototype); // "Apply" the inheritance
Box.prototype.constructor = Box; // Redefine the constructor to Box because it was overriden by the previous line
I disagree about those saying that you doesn't need "classes" in Javascript. In implementations such as Node.js which will handle datas must have, in my opinion, classes. It's easier (always in my opinion) to read, maintain, and use.

You can use the prototypal style like what Shryme explained, or you can use a library that mimcs the classical oop style in javascript, check Classing{js} : http://www.classingjs.co.nf/

Related

Passing values into p5's canvas, instancing

Let's say I have several p5 canvases on one page, and I've chosen to use instancing to split them up. Because many of the methods will perform the same operations, I've made a superclass. It looks something like this:
var BasicCanvas = function(p) {
p.setup = function() {
p.createCanvas(100, 100);
p.background(0);
}
p.draw = function() {
}
}
I'll have to create a subclass and pass this into a new p5 object, like this:
var myP5 = new p5(BasicCanvas);
How should I go about creating a subclass of my super in such a way it can be passed into the p5 constructor function? My ideal code setup looks like:
var super = function(p) {
p.setup = function() { creation }
}
var sub1 = function(p) {
p.draw = function() { circles }
}
var sub2 = function(p) {
p.draw = function() { squares }
}
var myP5 = new p5(sub1);
var myP52 = new p5(sub2);
But coming from Java, I'm having some difficulty getting around the differences. Can you help?
Thank you!
var superFunction = function(p) {
p.setup = function() {
p.createCanvas(200, 150)
p.noStroke()
}
}
var sub1 = function(p) {
superFunction(p)
p.draw = function() {
p.background(50)
p.fill("blue")
p.rect(20,20,50,50)
}
}
var sub2 = function(p) {
superFunction(p)
p.draw = function() {
p.background(100)
p.fill("red")
p.rect(20,20,50,50)
}
}
var myP5 = new p5(sub1)
var myP52 = new p5(sub2)
Note: super is a reserved keyword in Javascript

Object.assign() a deep object

I have a base object ProfileDialog which I am extending with Object.assign().
var ProfileDialog = function (containerObj) {
this.init = function () {
this.container = containerObj;
};
this.render = function () {
let content = document.createElement('div');
content.innerText = 'Dialog here';
this.container.appendChild(content);
};
this.init();
this.render();
};
Mixin:
var DialogMixin = function () {
return {
open: function () {
this.container.style.display = 'block';
},
close: function () {
this.container.style.display = 'none';
}
}
};
Now I do the assignment:
Object.assign(ProfileDialog.prototype, DialogMixin());
It works just fine, this context resolves fine in open and close methods.
But, when I put the mixin in a deeper structure, putting it inside actions property:
var DialogMixin = function () {
return {
actions: {
open: function () {
this.container.style.display = 'block';
},
close: function () {
this.container.style.display = 'none';
}
}
}
};
The context becomes actions object so the code breaks.
How do I properly extend the object with new methods when they are put in a deep structure?
The only thing i can think of is using bind to bind this.
So something like
var ProfileDialog = function (containerObj) {
this.containerObj = containerObj;
};
var DialogMixin = function (newThis) {
var obj = {
actions: {
open: function () {
console.log('open', this, this.containerObj.style);
}
}
}
obj.actions.open = obj.actions.open.bind(newThis);
return obj;
};
var container = {
style : 'some style'
};
var dialog = new ProfileDialog(container);
var mixinDialog = Object.assign(dialog, DialogMixin(dialog));
mixinDialog.actions.open();
See https://jsfiddle.net/zqt1me9d/4/

Is it possible to unit-test this javascript structure?

Given the following JavaScript structure:
addClickEvent: function() {
element.addEventListener('click', function() {
self.a();
self.b();
});
},
Is it possible to assert that a() and b() have been called without refactoring out the anonymous function or editing it's contents?
Assuming the self in your code is the window.self property.
You could do something like this:
function element_onclick_callsAandB() {
// Arrange
var aCalled = false;
var bCalled = false;
var element = ...;
var origA = self.a;
var origB = self.b;
self.a = function() {
aCalled = true;
origA();
};
self.b = function() {
bCalled = true;
origB();
};
try {
// Act
element.click();
// Assert
assertTrue(aCalled);
assertTrue(bCalled);
}
finally {
self.a = origA;
self.b = origB;
}
}

Extend prototype with another prototype

How can I extend prototype A with prototype B, so whenever I call prototype A, both will be executed?
var Helper = function() {
}
Helper.prototype.resizer = function() {
$('body').append(' Window resized ');
}
var Something = function() {
// Extend Helper.resizer with Something.anything
// extend(Helper.resizer, this.anything);
}
Something.prototype.anything = function() {
$('body').append(' Run this on resize to ');
}
var help = new Helper();
var some = new Something();
$(window).on("resize", function(){
help.resizer();
});
Made an example at codepen:
http://codepen.io/robbue/pen/892c8f61e1b5a970d6f694a59db401a6
jQuery allowed, or just vanilla.
I don't really understand your question because prototypes are not executed, but I think you want something like this:
var Helper = function() {}
Helper.prototype.resizer = function() {
$('body').append(' Window resized ');
}
var Something = function(h) {
var oldresizer = h.resizer,
that = this;
h.resizer = function() {
var res = oldresizer.apply(this, arguments);
that.anything();
return res;
};
}
Something.prototype.anything = function() {
$('body').append(' Run this on resize to ');
}
var help = new Helper();
new Something(help);
$(window).on("resize", function(){
help.resizer();
});
or that:
function Helper() {}
Helper.prototype.resizer = function() {
$('body').append(' Window resized ');
}
function Something() { // inherits Helper
Helper.apply(this, arguments);
}
Something.prototype = Object.create(Helper.prototype);
Something.prototype.anything = function() {
$('body').append(' Run this on resize to ');
};
Something.prototype.resizer = function() {
Helper.prototype.resizer.call(this);
this.anything();
};
var help = new Something(help);
$(window).on("resize", function(){
help.resizer();
});

"This" not refering to current object

I am kind of new to OOP in JS. I would like to know why when creating sub-objects, this stops referring to the main object AFTER the second level of subobjects.
function Clase()
{
this.__construct = function()
{
this.paginator();
alert('__construct finished');
};
this.paginator = function()
{
this.paginator.title = function()
{
this.paginator.title.set_offsets = function()
{
alert('paginator.title.set_offsets executed!');
};
};
this.paginator.title(); //instantiating
alert('subobject paginator created');
};
this.__construct();
}
var instancia = new Clase();
instancia.paginator.title.set_offsets();
http://jsfiddle.net/WYWwE/
The error is: this.paginator is undefined.
And now, if I use closures, it works perfectly:
function Clase()
{
self = this;
this.__construct = function()
{
this.paginator();
alert('__construct finished');
};
this.paginator = function()
{
self.paginator.title = function()
{
self.paginator.title.set_offsets = function()
{
alert('instancia.paginator.title.set_offsets() executed');
};
};
self.paginator.title();
alert('this.paginator created');
};
this.__construct();
}
var instancia = new Clase();
instancia.paginator.title.set_offsets();
http://jsfiddle.net/esjHu/
So, AFAIK after some point, "this" stops refering to the class "Clase" and refers to something else. If so, is it a good practice to use closures this way?
Is it also correct to start the class with self = this; and from then on use ONLY "self"? for instance: http://jsfiddle.net/byGRX/
You lose the reference to the "original" this when you nest functions. To remedy do the following:
function Clase() {
var that = this;
this.paginator = {
title: {
set_offsets: function() {
alert('paginator.title.set_offsets executed!');
}
}
};
};
var foo = new Clase();
foo.paginator.title.set_offsets();​
http://jsfiddle.net/vd5YK/
You don't lose reference to the this object, here's what happens:
For example:
function Class() {
this.func1 = function () {
this.func1.func2 = function () {
alert('Works!');
};
};
this.func1.func2();
}
x = new Class();
Now, the reason you get an error saying that func2 doesn't exist is because the function object for func2 isn't constructed until you call func1:
function Class() {
this.func1 = function () {
this.func1.func2 = function () {
alert('Works!');
};
};
this.func1();
this.func1.func2();
}
x = new Class();
And now it works.
EDIT:
So, why doesn't this work:
function Class() {
this.func1 = function() {
this.func1.func2 = function() {
this.func1.func2.func3 = function() {
alert('works!');
};
this.func1.func2.property = 5;
};
};
this.func1();
this.func1.func2();
}
x = new Class();
x.func1.func2.func3();
Basically, what your trying to do is add a property named property and a method named func3 to the function object of func2, but the problem is that func2 isn't constructed before calling func1. It's the same as doing:
function Class() {
this.func1 = function() {
this.func1.func2 = function() {};
};
this.func1.func2.func3 = function() {
alert('works!');
};
this.func1.func2.property = 5;
this.func1();
this.func1.func2();
}
x = new Class();
x.func1.func2.func3();
If you want it to work you need first construct the function object for func2 by calling func1:
function Class() {
this.func1 = function() {
this.func1.func2 = function() {};
};
this.func1();
this.func1.func2.func3 = function() {
alert('works!');
};
this.func1.func2.property = 5;
// this.func1.func2();
}
x = new Class();
x.func1.func2.func3();
alert(x.func1.func2.property);

Categories

Resources