This is my first trial with OOP so please be gentle..
First I created a function called Connection and I created objects out of it. There are currently 2 objects hardcoded, but there can by multiple objects dynamically created later on. How do I get the number of created objects (in this sample the result should be "2")? Or is there any smarter way to loop through all created objects?
function Connection(origin, target, departuredate, arrivaldate) {
this.origin = origin;
this.target = target;
this.departuredate = departuredate;
this.arrivaldate = arrivaldate;
}
var fare1 = new Connection("FRA", "BKK", "25.06.2018", "10.07.2018");
var fare2 = new Connection("FRA", "LAX", "22.07.2018", "30.07.2018");
In javascript functions are objects also and can have properties. Based on comments by georg and to avoid a global variable, why not just give Connection an array property and then use it however you want?
function Connection(origin, target, departuredate, arrivaldate) {
this.origin = origin;
this.target = target;
this.departuredate = departuredate;
this.arrivaldate = arrivaldate;
Connection.instances.push(this);
}
Connection.instances = [];
var fare1 = new Connection("FRA", "BKK", "25.06.2018", "10.07.2018");
var fare2 = new Connection("FRA", "LAX", "22.07.2018", "30.07.2018");
console.log(Connection.instances);
console.log('Number of connections:', Connection.instances.length);
Edit:
Keep in mind that this is a naive approach. It does not remove an instance of a Connection ever from the instances property. Furthermore it will keep references to those instance around forever, probably not what you want to do. I leave all of that as an exercise for the reader as, technically, the question did not ask for or about any of it.
You could use a global var in the constructor.
var connectionNb = 0;
function Connection(origin, target, departuredate, arrivaldate) {
this.origin = origin;
this.target = target;
this.departuredate = departuredate;
this.arrivaldate = arrivaldate;
++connectionNB;
}
var fare1 = new Connection("FRA", "BKK", "25.06.2018", "10.07.2018");
var fare2 = new Connection("FRA", "LAX", "22.07.2018", "30.07.2018");
Related
I am trying to populate an object by using a JavaScript class. I am not even sure if I am doing it correctly, I am very new to JavaScript OOP.
var ImagesViewModel = {}; // Global object
function ImagesClass() {
this.addImage = function (iUrl) {
ImagesViewModel.push({ "ImageUrl": iUrl }) //< Error is here
}
}
var k = new ImagesClass()
k.addImage("http://www.yahoo.com")
k.addImage("http://www.xbox.com")
Basically I need an easy way to populate ImagesViewModel with multiple properties. Do I need to specify properties within ImagesViewModel? Maybe I can do all of this without having to specify a global variable?
I am getting the error
Object has no method PUSH
What you want is an array and not an object, push is a method on Array prototype and you are trying to use it on object.
Change:
var ImagesViewModel = {};
To:
var ImagesViewModel = [];
You can do it this way as well so that each instance of ImagesClass has its own set of images.
function ImagesClass() {
var _images = [];
this.addImage = function (iUrl) {
_images.push({ "ImageUrl": iUrl }) //< Error is here
}
this.getImages = function(){
return _images;
}
}
and use it as:
var k = new ImagesClass();
k.addImage("http://www.yahoo.com");
k.addImage("http://www.xbox.com");
var ImagesViewModel = k.getImages(); //You can either set it directly or as a property of object
console.log(ImagesViewModel);
Demo
the push method is only for Arrays, here you are trying to push() to an object, which is why it isn't working.
You will need to change var ImagesViewModel = {}; to var ImagesViewModel = [];
From a design perspective, you probably don't want your viewmodel to just be a flat array (even though you declared it as an object, as other posters pointed out).
I'd suggest declaring an array declaration to hold the images inside of your ImagesViewModel object.
var ImagesViewModel = { // ViewModel generic OBJECT
this.Images = new Array(); // prototype ARRAY object
};
function ImagesClass() {
this.addImage = function (iUrl) {
ImagesViewModel.Images.push({ "ImageUrl": iUrl })
}
}
In the Google developers recommendation for optimizing JavaScript code, they mention that the best way to declare/initialize new variables for object is to use the prototype. For instance, instead of:
foo.Bar = function() {
this.prop1_ = 4;
this.prop2_ = true;
this.prop3_ = [];
this.prop4_ = 'blah';
};
Use:
foo.Bar = function() {
this.prop3_ = [];
};
foo.Bar.prototype.prop1_ = 4;
foo.Bar.prototype.prop2_ = true;
foo.Bar.prototype.prop4_ = 'blah';
However, in my case I have a dependency between variables, for instance:
var appv2 = function(){
this.start(this.person, this.car);
};
appv2.prototype.toWhom = 'Mohamed';
appv2.prototype.person = new person(this.toWhom);
appv2.prototype.car = new car();
appv2.prototype.start = function(person, car){
console.log('start for appv2 is called');
person.sayHello('me app v2');
car.brand();
};
new appv2();
Using this.toWhom outside of the main constructor body or a method function of the object will yield undefined. To solve this I could use appv2.prototype.toWhom instead of this.toWhom or I could declare my dependent variables inside of the main constructor body.
But I would like to know what is the best way, in terms of performance, to accomplish this?
Thanks
To reference toWhom while creating person, you can either store the value in a separate variable:
var toWhom = appv2.prototype.toWhom = 'Mohamed';
appv2.prototype.person = new person(toWhom);
Or, reference it from the prototype, as you suspected:
appv2.prototype.person = new person(appv2.prototype.toWhom);
The reason this.toWhom is undefined is because this doesn't refer to an instance of appv2 there.
I'm trying to make classical Collection/Instance model via javascript. So Collection object has some method for working with full collection and ((new Collection()) instanceof Instance) has methods to work with the instance. My code is rather simple.
var Collection = function Collection() {
this.message = "collection";
var I = Instance.bind(null, this);
I.__proto__ = this;
return I;
};
Collection.prototype = {
collectionMethod: function () {
console.log(this.message);
}
};
var Instance = function Instance(collection) {
this.collection = collection;
this.message = "instance";
};
Instance.prototype = {
instanceMethod: function () {
console.log(this.message);
}
};
// Test exec (values are like expected);
var C = new Collection();
var i = new C();
C.collectionMethod(); // collection
i.instanceMethod(); // instance
i.collection.collectionMethod(); // collection
C.newMethod(); // TypeError
i.newMethod(); // TypeError
Collection.prototype.newMethod = Instance.prototype.newMethod = function () {
console.log("newMethod: " + this.message);
}
C.newMethod(); // newMethod: collection
i.newMethod(); // newMethod: instance
But i don't want to use proto because it's not a part of standart and doesn't work in IE at all. Is there any way around in this case?
Some explanations about what's all about. For example you have a collection of users. And you want to be able find the user and create new one.
So you create new collection like
var User = new Collection();
Then you create new instance like.
var me = new User({name: "alex"});
And now you find this instance like
User.find_by_name("alex"); // === me
Also (in fact this is the main reason i'm doing this way instead of just creating something like User.new function to use it like var me = User.new({name: "alex"});) you can know who I am doing something like (if you for example have also var Dog = new Collection())
me instanceof Dog // false
me instanceof User // true
This code:
var I = Instance.bind(null, this);
I.__proto__ = this;
return I;
really doesn't make much sense. Function.bind creates a new function, so anyone calling your Collection function, in any way, will get back a function, not an object whose prototype is set to the function's prototype.
In general, if you want to create an object whose prototype is set to a specific object, you don't set __proto__ since that's not standard, as you stated. The best way is to just use Object.create (which is shimable if you want to support IE8).
var I = Object.create(this);
Also, the reason you're getting errors on newMethod is because you're trying to call them before you add them to the prototype:
Collection.prototype.newMethod = Instance.prototype.newMethod = function () {
console.log("newMethod: " + this.message);
}
C.newMethod(); // should work now
i.newMethod(); // should work now
So seems like it's impossible for now. More information can be found here.
How do I inherit javascript functions ?
I'm working on a wizard that uses javascript to change the page in an iframe. I'd like to create an object for each page of the wizard with references to a next & previous page.
Edit: The code posted below does not work. actionOne.nextAction is equal to {} after execution.
var actionOne = {};
var actionTwo = {};
actionOne = {
url: 'actionOneUrl.htm',
prevAction: null,
nextAction: actionTwo,
doDisplay: function(){
$('.label').html('Action One');
}
}
actionTwo = {
url: 'actionTwoUrl.htm',
prevAction: actionOne,
nextAction: null,
doDisplay: function(){
$('.label').html('Action Two');
}
}
The problem is that I can't figure out how to properly set up the next and previous references. There is likely a relatively simple solution, but I'm not sure what to search for. I am able to set the references after creating all the pages, but it feels very clunky to do so. Is there a way to do it while creating the objects?
For what you're trying to do, you're going to need to use an Object Oriented approach in JavaScript. This will allow you to assign a reference to new instances of your object. For example this works:
http://jsfiddle.net/Gq7vQ/
function Action(url, name){
this.url = url;
this.prevAction = null;
this.nextAction = null;
this.name = name;
}
Action.prototype.doDisplay = function(){
$(".label").html(this.name);
}
var actionOne = new Action('actionOneUrl.html', 'Action One');
var actionTwo = new Action('actionTwoUrl.html', 'Action Two');
actionOne.nextAction = actionTwo;
actionTwo.prevAction = actionOne;
console.log(actionOne.nextAction);
EDIT: So the OP asked for an implementation that automatically sets up these links between newly added actions. So here is a doubly-linked list implementation:
http://jsfiddle.net/wXC9B/1/
function ActionList(){
this.head = null;
this.tail = null;
}
ActionList.prototype.doDisplay = function(index){
var node = this.getNode(index);
console.log(node.name);
}
ActionList.prototype.getNode = function(index){
var current = this.head,
c = 0;
while(c < index && current !== null){
current = current.nextAction;
c++;
}
return current;
}
ActionList.prototype.add = function(url, name){
var node = {
url: url,
name: name,
nextAction: null,
prevAction: null
};
if(this.head === null){
this.head = node;
this.tail = node;
}
else{
this.tail.nextAction = node;
node.prevAction = this.tail;
//move tail to new node
this.tail = node;
}
}
var actionList = new ActionList();
//Each add automatically sets up links between the two
actionList.add('actionOneUrl.html', 'Action One');
actionList.add('actionTwoUrl.html', 'Action Two');
console.log(actionList.getNode(1));
actionList.doDisplay(1);
This is a very simplified example, but something like the following structure would prevent the need to manually reference your next/prev actions...let the application logic go find what to do based on the user's inputs.
UnderscoreJS's where function http://underscorejs.org/#where would be useful here
var dataFromServer = [
{id:"1", name: "First Page", nextId:"2"},
{id:"2", name: "Second Page", nextId:"3", prevId: "1"},
.....];
var actions = [];
var Action = function(data) {
this.doNextURL = function() {
//find action with id= data.nextId;
var actionToDo = _.where(actions, {id: data.nextId})[0];
window.location.href = actionToDo.url; //or something... a callback parameter, or returning the action rather than doing the 'ui logic' here would be better real world
}
}
for(var i = 0; i < dataFromServer.length; i+=1){
actions.push(new Action(dataFromServer[i]));
}
When you do
actionTwo = {
// ...
}
you are assigning a new value to actionTwo. It does not refer to the object anymore you assigned in var actionTwo = {}; and hence does not refer to the object you used in
actionOne = {
// ...
nextAction: actionTwo,
// ...
}
The easiest way would be to just initialise both objects and then assign them to the correct properties later on:
var actionOne = {
url: 'actionOneUrl.htm',
prevAction: null,
nextAction: null,
doDisplay: function(){
$('.label').html('Action One');
}
};
var actionTwo = {
url: 'actionTwoUrl.htm',
prevAction: null,
nextAction: null,
doDisplay: function(){
$('.label').html('Action Two');
}
};
actionOne.nextAction = actionTwo;
actionTwo.prevAction = actionOne;
If you want to do this for multiple actions, you should consider using constructor functions, so as joeltine shows in his answer.
To learn more about objects, have a look at MDN - Working with Objects.
When you use the {} object literal to define an object you are creating a new object instance with the Object constructor.
The following creates two new object instances from the Object constructor:
var actionOne = {}; // object instance 1
var actionTwo = {}; // object instance 2
This next part creates another new object instance (the third object instance) from the Object constructor and adds several properties. actionOne.nextAction points to the object instance of actionTwo (which doesn't have any of its own properties).
actionOne = {
url: 'actionOneUrl.htm',
prevAction: null,
nextAction: actionTwo,
doDisplay: function(){
$('.label').html('Action One');
}
} // object instance 3
So now when you declare actionTwo = {....} it creates a fourth object instance with a bunch of new properties. actionOne.prevAction still points to the second object instance you created (but are are no longer referencing with the the global variable actionTwo).
The key to remember is that the object literal {} creates new object instances with the Object constructor and the properties you create reference the object instance at the time they are declared.
Try this: don't create NEW objects for actionOne, actionTwo, instead leave your code as is - but assign to object properties of the already existing objects (which the first two lines create).
var actionOne, actionTwo;
actionOne = {
url: 'actionOneUrl.htm',
doDisplay: function(){
$('.label').html('Action One');
}
};
actionTwo = {
url: 'actionTwoUrl.htm',
doDisplay: function(){
$('.label').html('Action Two');
}
};
actionOne.prevAction = null; //could also be set above
actionOne.nextAction = actionTwo;
actionTwo.prevAction = actionOne;
actionTwo.nextAction = null; //could also be set above
Your question was a very good one - don't let anyone tell otherwise :) It is NOT obvious, even with quite a bit of JS background, that the object properties point to the objects the variables pointed to at the time the (literal) object creation statement was executed, rather than to the variable itself (in which case your example would have worked).
And please ignore the MVC pattern thing, even if it was even upvoted. Nothing wrong with MVC (sometimes), but this is a much, much MUCH more basic Javascript question, those pattern things come into play on a whole different (higher) level than your little interesting issue.
Some background: Deep inside the bowels of the Javascript execution engine variables that have an object as value are pointers (C/C++ background knowledge is good for understanding Javascript, because JS engines are written in it). So, when you assign the value of such a variable to an object property it will not point to the variable, but instead it will receive the pointer the variable has at value at the time. This means if the variable gets a new object assigned, pointing to another place in memory, the object property keeps pointing to the old object. If it pointed to the variable instead it would there find a pointer to the new object. As you can see, answering your question leads us deep inside how Javascript engines actually work on a very low level :)
All the other answers sure also solve your immediate issue, but I believe knowing this bit of background is much more fertile, in the end. Instead of trying to just give an answer that works it's sometimes worth investigating what's really going on... :)
Primitive types are stored in the variable directly, variables for objects are actually pointers. new String("foo") is an object (String), "foo" is a primitive type (string).
The exact same issue is important to keep in mind when calling functions in Javascript! It is call by value always, technically - but when the variable is a pointer to an object the value IS the pointer, which one must consider when assigning to variables the function gets as parameter.
Everyone seems to really be overcomplicating this.
function Wizard(o) {
return { url:o.url, doDisplay:function() { $('.label').html(o.html); } };
}
var wizards = [{url: 'actionOneUrl.html', html:'Action One'},
{url: 'actionTwoUrl.html', html:'Action Two'}].map(Wizard);
// wizards now contains an array of all your wizard objects
wizards.reduce(function(p,c) { c.prevAction = p; return p.nextAction = c; });
// prevAction and nextAction now point to the right places
No you can't, but you can keep the empty objects and just fill them:
var actionOne = {};
var actionTwo = {};
actionOne.url = 'actionOneUrl.htm';
actionOne.prevAction = null;
actionOne.nextAction = actionTwo;
...
But that's rather ugly. I would recommend filling in the links between them by using a function like this:
function chain() {
var i, prev, curr;
for(i = 0; i < arguments.length; i++) {
curr = arguments[i];
if(prev) {
prev.nextAction = curr;
curr.prevAction = prev;
}
else {
curr.prevAction = null;
}
prev = curr;
}
if(curr) curr.nextAction = null;
}
var actionOne, actionTwo;
actionOne = {
url: 'actionOneUrl.htm',
doDisplay: function(){
$('.label').html('Action One');
}
}
actionTwo = {
url: 'actionTwoUrl.htm',
doDisplay: function(){
$('.label').html('Action Two');
}
}
chain(actionOne, actionTwo);
Say I have this code:
function ParentClass()
{
var anArray = [ ];
this.addToArray = function(what)
{
anArray.push(what);
console.log(anArray);
};
}
FirstSubClass.prototype = new ParentClass();
FirstSubClass.prototype.constructor = FirstSubClass;
function FirstSubClass()
{
this.addToArray('FirstSubClass');
}
SecondSubClass.prototype = new ParentClass();
SecondSubClass.prototype.constructor = SecondSubClass;
function SecondSubClass()
{
this.addToArray('SecondSubClass');
}
When I run new FirstSubClass() I see a single value array in the console. And when I run new SecondSubClass(), again, I see a single value array.
However, why is it when I run them again (i.e. new FirstSubClass(); new SecondSubClass();) I then see the arrays added to rather than new ones being created?
The rationale here is that I'm creating new instances of a class, therefore why are they sharing the same private property?
How can I avoid this so when I do, for e.g., new FirstSubClass() I then see a single value array no matter how many times I create a new instance of the class?
Keep in mind that you've only called new ParentClass() once for each subclass. That means that the private array variable is part of the prototype object for those subclasses. There's only one prototype object, so there's only one array (per subclass).
Each call to new FirstSubClass() generates a new instance that shares the same prototype object. The call to addToArray() therefore adds an element to that same array that was created when the prototype object was created.
edit — if you want per-instance arrays, you'd have to do something like this:
function ParentClass() {
this.addToArray = function(value) { this.instanceArray.push(value); };
};
function FirstSubClass() {
this.instanceArray = [];
this.addToArray("First");
}
FirstSubClass.prototype = new ParentClass();
FirstSubClass.prototype.constructor = FirstSubClass;
First, sub-classing in JS is typically a bad idea, because people think that they're getting extension, where every instance has its own copy of properties and methods...
...really, they're getting public static access to the parent's stuff.
Even better, that public static stuff has no access to the encapsulated variables, so there's really no manipulation of private data, unless you're using private functions (with a public interface) to pass data to and collect return values from, the public static stuff.
var Parent = function () {
this.static_prop = 0;
this.static_method = function (num) { this.static_prop += 1; return num + this.static_prop; };
};
var Child = function (num) {
this.public_func = function () { num = this.static_method(num); };
};
Child.prototype = new Parent();
var child = new Child(13);
child.public_func();
Just calling this.static_method wouldn't help, because it would have 0 access to num, which means that you're wrapping things which you inherited to grant them access to use private data as inputs, which means that you're doing most of the writing you'd be doing anyway, regardless of inheritance, because your expectations of .prototype were backwards.
Might I suggest Dependency Injection, instead?
Component-based programs?
var Iterator = function () {
var count = 0,
min = 0,
max = 0,
reset = function () { count = min; },
next = function () { count = count >= max ? min : count; return count += 1; },
set_min = function (val) { min = val; },
set_max = function (val) { max = val; },
public_interface = { reset : reset, count : count, set_min : set_min, set_max : set_max };
return public_interface;
},
Thing = function (iter) {
var arr = [],
currentObj = null,
nextObj = function () {
currentObj = arr[iter.next()];
},
add = function (obj) {
arr.push(obj); iter.set_max(arr.length);
},
public_interface = { next : nextObj, add : add };
return public_interface;
};
var thing = Thing(Iterator());
thing.add({});
thing.next();
It's a convoluted example, but now every instance is going to be given exactly what it needs to do its job (because the constructor requires it -- or you can add the dependency later, through a public method, or as a public-property).
The interfaces for each module can now also get as simple and as clean as you'd like, as you don't have to wrap unexpected static-helpers to get private data...
Now you know what's private, you know what you're extending to the public, and you have clean ins and outs wherever you want to put them.
You are only constructing a new instance of ParentClass once per subclass and that is to apply it to your prototype. If you want each instance to have its own copy of the private array and its own copy of the function "addToArray" you will need to invoke the ParentClass constructor function within your other objects constructors:
function ParentClass(){
var anArray = [ ];
this.addToArray = function(what){
anArray.push(what);
console.log(anArray);
};
}
FirstSubClass.prototype = new ParentClass();
FirstSubClass.prototype.constructor = FirstSubClass;
function FirstSubClass(){
//call the parents constructor where "this" points to your FirstSubClass instance
ParentClass.call( this );
this.addToArray('FirstSubClass');
}
SecondSubClass.prototype = new ParentClass();
SecondSubClass.prototype.constructor = SecondSubClass;
function SecondSubClass(){
ParentClass.call( this );
this.addToArray('SecondSubClass');
}
try this:
http://jsfiddle.net/3z5AX/2/
function ParentClass()
{
var anArray = [ ];
this.addToArray = function(what)
{
anArray.push(what);
document.getElementById("i").value = anArray;
};
}
//FirstSubClass.prototype = new ParentClass();
FirstSubClass.prototype.constructor = FirstSubClass;
function FirstSubClass()
{
this.parent = new ParentClass()
this.parent.addToArray('FirstSubClass');
}
var q = new FirstSubClass();
var r = new FirstSubClass();
All Subclasses share the same parent class, thus the same private anArray
The solution is to use the Mixin pattern.
// I have the habbit of starting a mixin with $
var $AddToArray = function(obj) {
var array = [];
obj.addToArray = function(what) {
array.push(what);
console.log(array);
};
}
var FirstClass = function() {
$AddToArray(this);
}
var SecondClass = function() {
$AddToArray(this);
}