How to handle several instance on the same Class in a plugin? - javascript

I have a JS plugin using es6 class syntax. I'm not sure on the way to handle several instances of the class versus once instance with a several element inside.
This plugin can have an array an unlimited number of image nodes as parameters.
This is the class syntax I have so far
(function(window) {
function handle(element, options) {
let handles = [];
if (element.length) {
for (var i = 0; i < element.length; i++) {
handles.push(new Plugin(element[i], options));
}
} else {
handles.push(new Plugin(element, options));
}
return handles;
}
class Plugin {
constructor(element, options) {
this.element = element;
this.init();
}
init() {
//get its translated value
this.methodA();
//apply its translation even if not visible for the first init
this.methodB();
}
methodA() {
this.element.classList.add('test');
}
}
return handle;
});
I would like to get rid of this handle function. What is the other way to have an instance of plugin for every element? and to be able to have the classPlugin at the top level without the need for this handle function.
I don't see any other way that having several instances of the class, because each instance get specified info for each image (height, offset, etc). Maybe I am missing something obvious here...

You can't actually instantiate an instance of a class without a loop. You may try eval. But it's not recommended. It's a bad practice.
Now let me explain why it is not possible.
JavaScript does not have classes and instances, it has only objects, which can delegate to other objects.
To create two objects based on a single object, but behind the scenes, there aren’t really two ‘instances’ of the Point object, there are just two objects that delegate to the original one. When you use new, JavaScript is actually just creating an object and setting its prototype to the object returned by the constructor function. Imagine if the example had been expanded to include a shared method like this:
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.logCoords = function () {
console.log(this.x, this.y);
};
var a = new Point(1, 2);
console.log(a.x); // logs '1'
a.logCoords(); // logs '1 2'
Behind the scenes, what’s happening is something more like this:
var Point = function (x, y) {
this.x = x;
this.y = y;
};
Point.prototype.logCoords = function () {
console.log(this.x, this.y);
};
var a = {};
a.__proto__ = Point.prototype; // see note below about this
a.constructor = Point;
a.constructor(1, 2);
console.log(a.x); // logs '1'
a.logCoords(); // logs '1 2'

Related

Accessing a class function with array of objects

I was coding in p5.js, and I noticed a problem that I couldn't pass.
I have a class named "Boxes". I am already using the functions that "Boxes" have. But while I tried to use that functions apply to an array of objects, It didn't work. How can I fix this problem?
class Boxes
{
constructor()
{
this.x;
this.y;
this.r=222;
this.g=55;
this.b=111;
}
show()
{
fill(this.r,this.g,this.b);
rect(this.x,this.y,50,50);
}
}
For standard variable it works perfectly like this.
var box1 = new Boxes();
box1.show(); // It works.
When I tried something different it doesn't work. The example below.
var myboxes = [{'x':this.x, 'y':this.y}]; // That's OK :)
myboxes.push({x:100, y:100}); // That's OK too :)
myboxes[1].show(); // But. It gives an error :/
It says: "myboxes[1].show is not a function"
Although I write the show() function, with parentheses. It says
"myboxes[1].show is not a function" It works fine when I use
box1.show(). How can I access the functions using an array of objects?
Shall I try something else? What are you suggesting?
If you want to have an array of Boxes, you can .push() the new objects like:
class Boxes {
constructor(param) {
this.x = param.x; //Assign the x
this.y = param.y; //Assign the y
this.r = 222;
this.g = 55;
this.b = 111;
}
show() {
console.log(this.x, this.y); //Test code,
//fill(this.r,this.g,this.b);
//rect(this.x,this.y,50,50);
}
}
var myboxes = [];
myboxes.push(new Boxes({x: 3,y: 20})); //Create a new box and push to the array
myboxes.push(new Boxes({x: 30,y: 200})); //Create anothe one and push to the array
myboxes[1].show(); //<-- You can show the x and y of element 1
If you create a non-Boxes object, it doesn't have show anywhere in its prototype chain. But that's OK, if you have access to the class, you can call the prototype method with your non-Boxes object as the this:
class Boxes {
show() {
console.log(this.x);
}
}
var myboxes = [{'x':this.x, 'y':this.y}];
myboxes.push({x:100, y:100});
Boxes.prototype.show.call(myboxes[1]);
But note that you'll also need to put r, g, and b properties on your non-Boxes object in order for show to work.

Is there a way to use a function parameter as a getter in a prototype?

Im trying to code pacman at the moment, and a came across a problem:
Since all the Ghosts use the same pathfinding and are generally pretty simular, I want to use a prototype for them. The only property in which they really differ, is the way, they choose their target position. I'd like to give a function to the prototype and use it as a getter. Is that possible?
function Ghost(color,x,y,getterFunction){
this.color = color;
this.x = x;
this.y = y;
this.direction = "up";
this.move = function(){
//Pathfind towards this.target
}
this.target = getterFunction; //or something like this...
}
Thanks for your help :^)
#Bergi is right. You don't want to use it as a getter. If you tried to add it to the prototype it would be overwritten by every new ghost you create, since the prototype is a shared object.
The prototype is for shared functionality. Instance functionality belongs in the instance, i.e. In your constructor function.
Your move function should be on the prototype. But target should be an instance method. You could set a default method for target on the prototype. Any instance method would be called before looking to the prototype.
Example
function Ghost(color, x, y, target){
// everything in here is instance specific
this.color = color;
this.x = x;
this.y = y;
this.direction = "up";
if (typeof target === 'function') {
this.target = target;
}
}
// this is the prototype
Ghost.prototype.move = function() {
//Pathfind towards this.target
this.target()
}
Ghost.prototype.target = function() {
// default target to use if no target provided at creation
}
So now, when you do this:
var redGhost = new Ghost('red', 0, 0, function() {
//do something unique
})
You'll have a ghost that's red and has a custom target function. But if you do this:
var yellowGhost = new Ghost('yellow', 0, 0)
You'll have a ghost that uses the default target function you added to the prototype.

Subclass of a Subclass?

I've been writing a game engine, and I wanted to re-organize my code to make it more modular. Currently, I have a main function called Isometric that accepts the canvas to draw.
var iso = new Isometric('canvas');
So far so good. Next, I had .newMap() to create a new map.
var map = iso.newMap(10,1,10); // Creates 10x1x10 map
However, I want to change that since there might be some confusion with the .map property (since this returns an array called map).
I wanted the syntax to look like this:
iso.Map.create(10,1,10);
So I tried something like this:
function Isometric(id) {
this.Map = function() {
this.create = function() {
}
}
}
But when I went to access it, I realized that the second level of this still refers to the same first level this. So, I can't create a sub-class of the Map object.
I've looked at a few different methods, but none of them had clear examples and I couldn't get them to work.
Among them, I'm aware that you can use prototype, and Object.create() but I've not gotten much success.
How can I do this?
The other solution I have is to do something like this:
function Isometric('id') {
this.Map = {
'create': function() { },
'load': function() {}
}
}
and then access it like
iso.Map['create'];
but I don't like that at all. Any clean methods of doing it?
My main interest is an example with the third-level method ..create() inside .map. If you could provide me with documentation related to my question that I have not yet found, that would be nice. But even mozilla docs didn't seem to help.
I think the appropriate thing to do here is to namespace your Map constructor under the Isometric constructor. Here is how you could go about it.
function Isometric(id) {
this.id = id;
}
Isometric.prototype = {
constructor: Isometric,
test: function() {
return "I'm an instance of Isometric.";
}
};
Here we do the namespacing and add a create() helper method to the Map constructor to create its instances.
Isometric.Map = function Map(x, y, z) {
this.x = x;
this.y = y;
this.z = z;
}
Isometric.Map.prototype = {
constructor: Isometric.Map,
test: function() {
return "I'm an instance of Isometric.Map.";
}
};
// Helper method to create `Map` instances
Isometric.Map.create = function create(x, y, z) {
return new Isometric.Map(x, y, z);
};
Usage:
var iso = new Isometric('id123');
var map = new Isometric.Map(0, 7, 99);
var map2 = Isometric.Map.create(1, 2, 3);
iso.test(); //=> "I'm an instance of Isometric."
map.test(); //=> "I'm an instance of Isometric.Map."
map2.test(); //=> "I'm an instance of Isometric.Map."
Namespaces
It's important to note that the namespacing we just did prevents collisions with the new Map class in ES6 (new JS version) - more about ES6 Maps here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map.
With that said, it's always important to namespace your code under one main object (you could call it app) and only make that namespace available globally.
In your case you could do something like the following example:
;(function(win) {
// Isometric
function Isometric(id) {
this.id = id;
}
Isometric.prototype = {
constructor: Isometric,
test: function() {
return "I'm an instance of Isometric.";
}
};
// Map
function Map(x, y, z) {
this.x = x;
this.y = y;
this.z = z;
}
Map.prototype = {
constructor: Map,
test: function() {
return "I'm an instance of Isometric.Map.";
}
};
// Helper method to create `Map` instances
Map.create = function create(x, y, z) {
return new Map(x, y, z);
};
// Namespace Map under Isometric
Isometric.Map = Map;
// Expose globally
// --------------------
win.app = {
Isometric: Isometric
};
}(this));
Example:
var nativeMap = new Map();
nativeMap.set('name', 'joe'); //=> {"name" => "joe"}
var myMap = new app.Isometric.Map(33, 7, 99);
myMap.test(); //=> "I'm an instance of Isometric.Map."
// Native map wasn't affected (good)
nativeMap.test(); //=> TypeError: undefined is not a function
I will leave the other answer as accepted because it was more detailed and even told me about my code possibly becoming deprecated as is.
That being said, I want to re-iterate Felix King's solution. I will figure a way of combining both ideas (using namespaces and using Felixs) but here's what the partial solution was.
function Isometric(id) {
this.Map = function() {
this.create = function() {
return 5;
};
};
}
That was the set up I had. 5 is just an arbitrary value.
var iso = new Isometric(); // New instance of Isometric()
var mapping = new iso.Map(); // Access to Iso's mapping functions
var map = mapping.create(); // Should return a map array
console.log(map); // Prints 5
For anyone following along on this, if you want to see how I end up implementing it, then see my Github project: https://github.com/joshlalonde/IsometricJS
I'll play around with the code in sometime this weekend.
Again, thanks for your help everyone! Good luck to anyone trying to do something similar.

Extending a javascript object

I'm currently working on a platform game engine using javascript and the HTML5 canvas.
I have an object, "platform" which looks something like this...
var platform = function(pid,px,py,pw,ph) {
//Some variables here... and then we have some functions
this.step = function() {
//Update / step events here
}
this.draw = function() {
//Drawing events here
}
//etc.
}
The step() function has all of the calculations for collision detection while the draw() function draws the platform.
What I want to do is make another object called movingPlatform. This will be almost identical to the current platform except for the fact this one moves.
Rather than copying all of the collision detection code I'd like to be able to extend movingPlatform from platform... and then be able to add some additional code into the step() function to the moving platform can... well... move.
Some additional information...
When the game loads, it generates the level using data from a CSV file. I have an array, platforms[] that stores all of the platforms within it.
So to create a platform it looks like this...
platforms.push(new platform(i,data[1],data[2],data[3],data[4]));
I then make the platforms perform their step and draw events during the game's main step and draw events.
i.e.
for(var i=0; i<platforms.length; i++) {
platforms[i].step();
}
Any help would be awesome. Thanks!
I would use the platform class as a "base" object for the moving platform object.
I would do this via the prototype which is JavaScript's implementation of object oriented programming.
More info here How does JavaScript .prototype work?
+ many more articles on the web
You can use Javascript prototype inheritance functionality:
var baseItem = {
push: function(){alert('push');},
pull: function(){alert('pull')}
}
var childItem = {}
childItem.prototype = baseItem;
childItem.push = function(){
//call base function
childItem.prototype.push.call(this);
//do your custom stuff.
alert('I did it again.');
}
childItem.push();
Fiddle
Rather than pure inheritance, here, I'd go with prototype-extension, unless you build some big, ugly factory, just for the sake of saying that "MovingPlatform" inherited from "Platform" in a pure sense, it's not really what you'd expect it to be.
There are a few concerns (cheating, for one), but if your objects are all based wholly around this, and you're okay with people potentially hacking away in the console, then you don't really have much to worry about.
First, understand what you're doing inside of Platform:
var MyObject = function (a) {
this.property = a;
this.method = function (b) { this.property += b; };
};
Every time you make a new MyObject, you're creating a brand new version of the .method function.
That is to say, if you make 10,000 of these, there will be 10,000 copies of that function, as well.
Sometimes that's a very good and safe thing.
It can also be a very slow thing.
The problem is, because everything in your object is using this, and because nothing inside of the function changes, there's no benefit to creating new copies -- just extra memory used.
...so:
MyObject = function (a) {
this.property = a;
};
MyObject.prototype.method = function (b) { this.property += b; };
var o = new MyObject(1);
o.method(2);
o.property; //3
When you call new X, where X has properties/methods on its prototype, those properties/methods get copied onto the object, during its construction.
It would be the same as going:
var method = function (b) { this.property += b; },
o = new MyObject(1);
o.method = method;
o.method(2);
o.property; // 3
Except without the extra work of doing it yourself, by hand.
The benefit here is that each object uses the same function.
They basically hand the function access to their whole this, and the function can do whatever it wants with it.
There's a catch:
var OtherObj = function (a, b) {
var private_property = b,
private_method = function () { return private_property; };
this.public_property = a;
this.unshared_method = function () { var private_value = private_method(); return private_value; };
};
OtherObj.prototype.public_method = function () {
return private_property;
};
var obj = new OtherObj(1, "hidden");
obj.public_property; // 1
obj.unshared_method(); // "hidden"
obj.public_method(); // err -- private_property doesn't exist
So assuming you don't have much you care about staying private, the easiest way of doing this would be to make reusable function, which rely on this, which you then give to multiple prototypes, through extension.
// collision-handling
var testCollision = function (target) { this./*...*/ },
handleCollision = function (obj) { this./* ... */ };
// movement-handling
var movePlatform = function (x, y, elapsed) { this.x += this.speed.x*elapsed; /*...*/ };
// not really the cleanest timestep implementation, but it'll do for examples
var Platform = function (texture, x, y, w, h) {
this.x = x;
// ...
},
MovingPlatform = function (texture, x, y, w, h, speedX, speedY, etc) {
this.etc = etc;//...
};
Platform.prototype.testCollision = testCollision;
Platform.prototype.handleCollision = handleCollision;
MovingPlatform.prototype. // both of the above, plus the movePlatform method
This is a lot by hand.
That's why functions in different libraries will clone or extend objects.
var bunchOfComponents = {
a : function () { },
b : 32,
c : { }
},
myObj = {};
copy(myObj, bunchOfComponents);
myObj.a();
myObj.b; //32
Your function-reuse goes up, while the horror of writing proper Class-based, hierarchical inheritance, with virtual-overrides, abstracts, and shared-private properties, by hand, goes down.
Getting inheritance right in Javascript is somewhat tricky if you're used to class-based languages.
If you're not sharing a lot of behaviours, you might find it easier to just create some shared methods, then make them available to objects of each platform type.
//Create constructors for each type
var Platform = function(pid,px,py,pw,ph) { //By convention, constructors should start with an uppercase character
...
}
var MovingPlatform = function() {
...
}
//Create some reuseable methods
var step = function() {
...
}
var draw = function() {
...
}
var move = function() {
...
}
//Attach your methods to the prototypes for each constructor
Platform.prototype.step = step;
Platform.prototype.draw = draw;
MovingPlatform.prototype.step = step;
MovingPlatform.prototype.draw = draw;
MovingPlatform.prototype.move = move;
...etc
That said, if you do want to build up a proper inheritance chain, there are plenty of articles available to help you: 1 2 3 4

How to add context methods to an object

I'm making a library for math graphing in canvas, and before, my approach was to directly add methods to the global context prototype, as such
CanvasRenderingContext2D.prototype.point=function(x,y){
this.fillRect(x,y,1,1);
};
However, I found out that that was not recommended, so what I'm trying now is to make a global object, as such
window.Graph=function(context){
this.ctx=context;
alert(this.ctx);
this.CanvasRenderingContext2D.prototype.point=function(x,y){
this.ctx.fillRect(x,y,1,1);
};
};
I have also tried
this.ctx.prototype.point=function(x,y){
this.ctx.fillRect(x,y,1,1);
};
All of them return errors like cannot set property 'point' to undefined
The ideal way to call it would be
var g=new Graph(ctx);
g.point(5,5);
What would be the best way to do this?
Thanks
Here's what you are looking for:
function Graph(context) {
this.context = context;
}
Graph.prototype.point = function (x, y) {
this.context.fillRect(x, y ,1, 1);
}
var g = new Graph(context);
g.point(5, 5);
plalx shows a great design pattern...
Here's just another with a constructor:
var Graph = (function () {
// constructor
function Graph(context) {
// "class" properties go here
this.context = context;
}
// "class" methods added to prototype here
Graph.prototype.point = function (x,y) {
this.context.fillRect(x,y,1,1);
};
// return a self reference
return Graph;
})(); // make class available by self-executing
// instantiate your Graph class into a graph object
var graph = new Graph(context);

Categories

Resources