How do I get the object created during a constructor? - javascript

Beginner question here. I am trying to use the class syntax in javascript to make an object. I want to add the object as whole to an arraylist when it is created. I would also like to loop through that array later. My question: how do I add the actual object created in the constructor. Example code:
class Example {
constructor(x,y,z) {
this.x = x;
this.y = y;
this.z = z;
}
}
Ideally I want to, when an Example object is created, add it to an array of examples. Can I do this within the constructor function? Also, if I have an array of Examples: what is the correct syntax for for in looping through it.
var examples = []
for Example in examples

the constructor can simply add the object like this
var examples = []
class Example {
constructor(x,y,z) {
this.x = x;
this.y = y;
this.z = z;
examples.push(this)
}
}

Just push it into an array:
class Example {
constructor() {
Example.instances.push(this);
}
}
Example.instances = [];
And you can iterate that like:
for(const instance of Example.instances) {
//...
}

this is a little strange, but you should be able to add this line to the bottom of your constructor:
examples.push(this);

Related

Using bind to utilize another Javascript class's method

I have an old-style class in Javascript that I want to utilize another old-style class's behavior without a full subclassing. Something like this:
function Foo(x) {
this.x = x;
}
Foo.prototype.add1 = function() {
return this.x + 1
}
function Bar(n) {
this.x = 2*n;
}
Bar.prototype.add1 = function() {
// steal behavior from Foo.add1
return Foo.prototype.add1.call(this);
}
This works fine in a browser console:
> b = new Bar(3)
Bar {x: 6}
> b.add1()
7
Is there a way to use bind instead? Something like
function Bar(n) {
this.x = 2*n;
}
Bar.prototype.add1 = Foo.prototype.add1.bind(xyz);
except that there's no "this" object to use for xyz before the object actually exists.
The following code works, but seems wrong....
function Bar(n) {
this.x = 2*n;
this.add1 = Foo.prototype.add1.bind(this);
}
Is there a way to use bind in this way when setting up an old-style class's prototype?
You didn't need to use bind, just assign Bar.prototype.add1 = Foo.prototype.add1. bind method is useful for instances not for classes.

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

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'

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.

Declare a class property outside of a class method

See how x and y are declared in constructor:
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
is there an way to declare properties outside of functions for instance:
class Point {
// Declare static class property here
// a: 22
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
So I want to assign a to 22 but I am unsure if i can do it outside the constructor but still inside the class..
Initializing properties directly on a class in ES6 is not possible, only methods can currently be declared in this way. Same rules stand in ES7 as well.
However, it is a proposed feature that might come after ES7 (currently in stage 3). Here is the official proposal.
Additionally, the syntax the proposal is suggesting is slightly different (= instead of :):
class Point {
// Declare class property
a = 22
// Declare class static property
static b = 33
}
If you are using Babel, you can use the stage 3 settings to enable this feature.
Here's a Babel REPL example
The other way to do this in ES6, other than in the constructor, is to do it after the class definition:
class Point {
// ...
}
// Declare class property
Point.prototype.a = 22;
// Declare class static property
Point.b = 33;
Here's a good SO Thread diving into this topic some more
Note:
As Bergi mentioned in the comments, the suggested syntax:
class Point {
// Declare class property
a = 22
}
is just syntactic sugar to provide a shortcut for this code:
class Point {
constructor() {
this.a = 22;
}
}
Where both of those statements assign a property to an instance.
However, this isn't exactly the same as assigning to the prototype:
class Point {
constructor() {
this.a = 22; // this becomes a property directly on the instance
}
}
Point.prototype.b = 33; // this becomes a property on the prototype
Both would still be available via an instance:
var point = new Point();
p.a // 22
p.b // 33
But getting b would require going up the prototype chain while a is available directly on the object.
#nem035 is right that it is in proposal stage.
However, #nem035's sugggetion is one way to achieve it as class instance member.
// Declare static class property here
Seems you are looking to declare a static member. If yes,
JavaScript way is
class Point {
// ...
}
Point.a = '22';
The way you are actually expecting can be done in TypeScript
class Point {
static a = 22;
}
The compiled output will be same as above example
Point.a = '22';

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.

Categories

Resources