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);
Related
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");
JavaScript closures - is there any alternative to manually reseting inner variables?
Assuming we have the following closure:
var SearchItemModalModel = (function(){
var filter1;
var filter2;
var filter3;
[...]
var filterN;
var initVariables = function(){
filter1 = undefined;
filter2 = undefined;
filter3 = undefined;
[...]
filterN = undefined;
};
return {
Init: function(){
initVariables();
},
SomeFunction1 : function(){
},
SomeFunction2 : function(){
}
[...]
}
})();
The above is used to display a modal with filterable list of items. Every time user clicks "Show available items" - all filters should be set to undefined. User can display the modal as many times as he/she likes, without reloading the page.
Right now, I reset everything manually, by calling initVariables() inside the Init function - however every time there is a modification to the above JS "class", I have to manually add all new variables to the initVariables() function...
So the question is: is there any better way to do it - so that no manual reseting would be required? At first I thought something like this would work:
var searchItemModalModel = new SearchItemModalModel;
But now, after reading about JS closures, I don't think this is possible - or is it?
Seems to me like you are trying to re-implement objects.
Your example fits itself to the constructor pattern:
var SearchItemModal = function(){
var filter1;
var filter2;
var filter3;
[...]
var filterN;
var initVariables = function(){
filter1 = undefined;
filter2 = undefined;
filter3 = undefined;
[...]
filterN = undefined;
};
this.Init: function(){
initVariables();
};
this.SomeFunction1 : function(){
};
this.SomeFunction2 : function(){
};
[...]
};
Which you then use by creating new instances of it with new:
var searchItemModal = new SearchItemModal();
searchItemModal.Init();
I encourage you to read more about constructors and OOP in JavaScript here, closures are great but there's a reason objects exist, and the way you wrote your code, the filterN variables will stay private either way.
You can use array instead of variables:
var N = 5;
var filter;
var initVariables = function(){
filter = new Array(N);
};
// access variables by it's index (starting from 0)
// e.g.: filter[0]
EDIT: you can also hold all variables in one state object and initialize it with some default state like this:
var defaultState = {
filter1: undefined,
filter2: undefined,
filter3: undefined,
[...]
filterN: undefined
};
var SearchItemModalModel = (function(){
var state = defaultState;
var initVariables = function(){
state = defaultState;
};
[...]
// access variable by object key
// e.g.: state.filter1
If i have a Javascript object defined as:
function MyObj(){};
MyObj.prototype.showAlert = function(){
alert("This is an alert");
return;
};
Now a user can call it as:
var a = new MyObj();
a.showAlert();
So far so good, and one can also in the same code run another instance of this:
var b = new MyObj();
b.showAlert();
Now I want to know, how can I hold the number of instances MyObj?
is there some built-in function?
One way i have in my mind is to increment a global variable when MyObj is initialized and that will be the only way to keep track of this counter, but is there anything better than this idea?
EDIT:
Have a look at this as suggestion here:
I mean how can I make it get back to 2 instead of 3
There is nothing built-in; however, you could have your constructor function keep a count of how many times it has been called. Unfortunately, the JavaScript language provides no way to tell when an object has gone out of scope or has been garbage collected, so your counter will only go up, never down.
For example:
function MyObj() {
MyObj.numInstances = (MyObj.numInstances || 0) + 1;
}
new MyObj();
new MyObj();
MyObj.numInstances; // => 2
Of course, if you want to prevent tampering of the count then you should hide the counter via a closure and provide an accessor function to read it.
[Edit]
Per your updated question - there is no way to keep track of when instances are no longer used or "deleted" (for example by assigning null to a variable) because JavaScript provides no finalizer methods for objects.
The best you could do is create a "dispose" method which objects will call when they are no longer active (e.g. by a reference counting scheme) but this requires cooperation of the programmer - the language provides no assistance:
function MyObj() {
MyObj.numInstances = (MyObj.numInstances || 0) + 1;
}
MyObj.prototype.dispose = function() {
return MyObj.numInstances -= 1;
};
MyObj.numInstances; // => 0
var a = new MyObj();
MyObj.numInstances; // => 1
var b = new MyObj();
MyObj.numInstances; // => 2
a.dispose(); // 1 OK: lower the count.
a = null;
MyObj.numInstances; // => 1
b = null; // ERR: didn't call "dispose"!
MyObj.numInstances; // => 1
Create a static property on the MyObj constructor called say count and increment it within the constructor itself.
function MyObj() {
MyObj.count++;
}
MyObj.count = 0;
var a = new MyObj;
var b = new MyObj;
alert(MyObj.count);
This is the way you would normally do it in say Java (using a static property).
var User = (function() {
var id = 0;
return function User(name) {
this.name = name;
this.id = ++id;
}
})();
User.prototype.getName = function() {
return this.name;
}
var a = new User('Ignacio');
var b = new User('foo bar');
a
User {name: "Ignacio", id: 1}
b
User {name: "foo bar", id: 2}
Using ES6 Classes MDN syntax - we can define a static method:
The static keyword defines a static method for a class. Static methods are called without instantiating their class and cannot be called through a class instance. Static methods are often used to create utility functions for an application.
class Item {
static currentId = 0;
_id = ++Item.currentId; // Set Instance's this._id to incremented class's ID
// PS: The above line is same as:
// constructor () { this._id = ++Item.currentId; }
get id() {
return this._id; // Getter for the instance's this._id
}
}
const A = new Item(); // Create instance (Item.currentId is now 1)
const B = new Item(); // Create instance (Item.currentId is now 2)
const C = new Item(); // Create instance (Item.currentId is now 3)
console.log(A.id, B.id, C.id); // 1 2 3
console.log(`Currently at: ${ Item.currentId }`); // Currently at: 3
PS: if you don't want to log-expose the internal currentId property, make it private:
static #currentId = 0;
_id = ++Item.#currentId;
Here's an example with constructor and without the getter:
class Item {
static id = 0;
constructor () {
this.id = ++Item.id;
}
getID() {
console.log(this.id);
}
}
const A = new Item(); // Create instance (Item.id is now 1)
const B = new Item(); // Create instance (Item.id is now 2)
const C = new Item(); // Create instance (Item.id is now 3)
A.getID(); B.getID(); C.getID(); // 1; 2; 3
console.log(`Currently at: ${ Item.id }`); // Currently at: 3
what about such method?
var Greeter = (function ()
{
var numInstances;
function Greeter(message)
{
numInstances = (numInstances || 0) + 1;
this.greeting = message;
}
Greeter.prototype.greet = function ()
{
return "Hello, " + this.greeting;
};
Greeter.prototype.getCounter = function ()
{
return numInstances;
};
return Greeter;
})();
var greeter = new Greeter("world");
greeter.greet();
greeter.getCounter();
var newgreeter = new Greeter("new world");
newgreeter.greet();
newgreeter.getCounter();
greeter.getCounter();
Keeping a global count variable and incrementing every time is an option. Another option is to call counter method after each instance creation by hand (the worst thing I could imagine). But there is another better solution.
Every time we create an instance, the constructor function is being called. The problem is the constructor function is being created for each instance, but we can have a count property inside __proto__ which can be the same for each instance.
function MyObj(){
MyObj.prototype.addCount();
};
MyObj.prototype.count = 0;
MyObj.prototype.addCount = function() {
this.count++;
};
var a = new MyObj();
var b = new MyObj();
This is our a and b variables after all:
Eventually, JS is going to have built-in proxy capability, which will have low-level access to all kinds of things which happen in the background, which will never be exposed to front-end developers (except through the proxy -- think magic-methods in languages like PHP).
At that time, writing a destructor method on your object, which decrements the counter might be entirely trivial, as long as support for destruction/garbage-collection as a trigger is 100% guaranteed across platforms.
The only way to currently, reliably do it might be something like creating an enclosed registry of all created instances, and then manually destructing them (otherwise, they will NEVER be garbage-collected).
var Obj = (function () {
var stack = [],
removeFromStack = function (obj) {
stack.forEach(function (o, i, arr) {
if (obj === o) { arr.splice(i, 1); }
makeObj.count -= 1;
});
};
function makeObj (name) {
this.sayName = function () { console.log("My name is " + this.name); }
this.name = name;
this.explode = function () { removeFromStack(this); };
stack.push(this);
makeObj.count += 1;
}
makeObj.checkInstances = function () { return stack.length; };
makeObj.count = 0;
return makeObj;
}());
// usage:
var a = new Obj("Dave"),
b = new Obj("Bob"),
c = new Obj("Doug");
Obj.count; // 3
// "Dave? Dave's not here, man..."
a.explode();
Obj.count; // 2
a = null; // not 100% necessary, if you're never going to call 'a', ever again
// but you MUST call explode if you ever want it to leave the page's memory
// the horrors of memory-management, all over again
Will this pattern do what you want it to do?
As long as:
you don't turn a into something else
you don't overwrite its explode method
you don't mess with Obj in any way
you don't expect any prototype method to have access to any of the internal variables
...then yes, this method will work just fine for having the counter work properly.
You could even write a general method called recycle, which calls the explode method of any object you pass it (as long as its constructor, or factory, supported such a thing).
function recycle (obj) {
var key;
obj.explode();
for (key in obj) { if (obj.hasOwnProperty(key)) { delete obj[key]; } }
if (obj.__proto__) { obj.__proto__ = null; }
}
Note - this won't actually get rid of the object.
You'll just have removed it from the closure, and removed all methods/properties it once had.
So now it's an empty husk, which you could reuse, expressly set to null after recycling its parts, or let it be collected and forget about it, knowing that you removed necessary references.
Was this useful?
Probably not.
The only time I really see this as being of use would be in a game where your character might only be allowed to fire 3 bullets at a time, and he can't shoot a 4th until the 1st one on screen hits someone or goes off the edge (this is how, say, Contra worked, in the day).
You could also just shift a "disappeared" bullet off the stack, and reuse that bullet for any player/enemy by resetting its trajectory, resetting appropriate flags, and pushing it back onto the stack.
But again, until proxies allow us to define "magic" constructor/destructor methods, which are honoured at a low-level, this is only useful if you're going to micromanage the creation and destruction of all of your own objects (really not a good idea).
My solution is creating an object store instance count and a function to increase them in prototype.
function Person() {
this.countInst();
}
Person.prototype = {
constructor: Person,
static: {
count: 0
},
countInst: function() {
this.static.count += 1;
}
};
var i;
for (i = 0; i < 10; i++) {
var p = new Person();
document.write('Instance count: ');
document.write(p.static.count);
document.write('<br />');
}
Here is my plunker: https://plnkr.co/edit/hPtIR2MQnV08L9o1oyY9?p=preview
class Patient{
constructor(name,age,id){
Object.assign(this,{name, age, id});
}
static patientList = []; // declare a static variable
static addPatient(obj){
this.patientList.push(...obj); // push to array
return this.patientList.length; // find the array length to get the number of objects
}
}
let p1 = new Patient('shreyas',20, 1);
let p2 = new Patient('jack',25, 2);
let p3 = new Patient('smith',22, 3);
let patientCount = Patient.addPatient([p1,p2,p3]); // call static method to update the count value with the newly created object
console.log(Patient.patientList);
console.log(patientCount);
I'm designing an OOP inheritance pattern for many applications I'm building. Javascript has many ways of doing this, but I stumbled on a pattern I really like. But now I'm struggling with the need for a separation of classes and instances.
I have a base object called Root. And it has a main method called inherit. To create a new object you use
var Person = Root.inherit({
name : "",
height : 0,
walk : function() {},
talk : function() {}
});
Then to create an "instance" you would
var sally = Person.inherit({
name : "sally",
height : "5'6"
});
sally can .talk() and she can walk() and she has a .name and a .height
You can make more people the same way.
If you want a constructor you use
var Person = Root.inherit({
_construct : function() {
// do things when this object is inherited from
},
name : "",
height : 0,
walk : function() {},
talk : function() {}
});
It also has the ability to have init, when the object is first defined in code (singletons use this)
var Person = Root.inherit({
_init : function() {
// called at runtime, NOT called if an object is inherited from me
},
name : "",
height : 0,
walk : function() {},
talk : function() {}
});
So as you can see, everything uses .inhert(). There are no classes and no instances really. Everything is an instance of something. The only real problem I found so far is that there is no concept of "type", but you can always just check for a method if you need to. Also you can't protect a 'class', as a 'class' can be changed during execution if the developer accidentally changed it, or meant to change it.
So my question is: Is there a need in javascript to have an explicitly and controlled separation of class structure and instances of the class? Are there any issues with treating every object as an instance?
No there's no need since Javascript is a Prototypal based language, meaning that classes are not involved. You are just creating clones of the objects.
http://en.wikipedia.org/wiki/Prototype-based_programming
As far as the concept of type, the type is object.
A good read for more info about this would be Javascript Patterns by Stoyan Stefanov he has several different creational patterns that address your concerns, including examples that implement Design Patterns from the gang of four's design patterns.
http://www.amazon.com/JavaScript-Patterns-Stoyan-Stefanov/dp/0596806752
So my question is: Is there a need in javascript to have an explicitly and controlled separation of class structure and instances of the class? Are there any issues with treating every object as an instance?
Not really, if you're happy with it, it's fine.
The more normal form of JavaScript inheritance does much the same thing. You'll frequently see structures like this (severely cut down for brevity):
function Base() {
}
Base.prototype.foo = function() {
};
function Derived() {
}
Derived.prototype = new Base();
...and of course, new Base() is also how you create instances of Base. So your system is quite similar.
Again, the above is a sketch, not a full example. For one thing, usually you'd see construction and initialization separated out, so you don't literally see Derived.prototype = new Base() so much as something that creates an object with Base's prototype but without actually calling Base (which Derived would do later), but you get the idea. Granted that statement somewhat weakens the similarity with your system, but I don't think it breaks it at all.
At the end of the day, it's all about objects (instances), which are either used directly (your sally) or indirectly by providing features to other objects (Person, Root) by cloning or by setting them up as the prototype of the other object.
Javascript's inheritance is prototypical which means everything object is an instance. You actually have to do extra work to get the classical inheritance.
This is how I work in javascript
// this is class
function person(){
// data is member variable
this.name = null;
this.id = null;
//member functions
this.set_name = _set_name;
this.get_name = _get_name;
this.set_id = _set_id;
this.get_id = _get_id;
function _set_name(name){
this.name = name;
}
function _get_name(name){
return this.name;
}
function _set_id(id){
this.id = id;
}
function _get_id(id){
return this.id;
}
}
// this is instance
var yogs = new person();
yogs.set_id(13);
yogs.set_name("yogs");
hope it may help
Start with some basic object...
// javascript prototypes - callback example - javascript objects
function myDummyObject () {
that = this;
} // end function myDummyObject ()
// begin dummy object's prototype
myDummyObject.prototype = {
that : this,
// add a simple command to our dummy object and load it with a callback entry
say : function () {
var that = this;
console.log('speaking:');
that.cb.run("doSay");
}
} // end myDummyObject proto
extend with a sub prototype..
// here we addon the callback handler... universally self sufficient object
var cb = {
that : this, // come to papa ( a link to parent object [ myDummyObject ] )
jCallback : new Array(new Array()), // initialize a javascript 2d array
jCallbackID : -1, // stores the last callback id
add: function(targetFnc, newFunc) {
var that = this;
var whichID = that.jCallbackID++;
// target, addon, active
that.jCallback[that.jCallback.length] = { 'targetFunc' : targetFnc, 'newFunc' : newFunc, 'active' : true, 'id': whichID };
return whichID; // if we want to delete this later...
}, // end add
run: function(targetFnc) {
var that = this;
for(i=0;i <= that.jCallback.length - 1;i++) // go through callback list
if( that.jCallback[i]['targetFunc'] == targetFnc && that.jCallback[i]['active'] == true )
that.jCallback[i]['newFunc'](); // run callback.
}, // end run
remove: function (whichID) {
var that = this;
console.log('removing:' + whichID);
for(i=0;i <= that.jCallback.length - 1;i++) // go through callback list
if( that.jCallback[i]['id'] == whichID )
that.jCallback[i]['newFunc'](); // run callback.
} // end remove
}
// add the object to the dummy object...
myDummyObject.prototype.cb = cb;
Example:
var testing = new myDummyObject();
testing.cb.add('doSay', function () { console.log('test: 213123123'); } );
// test remove...
var testid = testing.cb.add('doSay', function () { console.log('test: 12sad31'); } );
testing.cb.remove(testid);
testing.cb.add('doSay', function () { console.log('test: asdascccc'); } );
testing.cb.add('doSay', function () { console.log('test: qweqwe'); } );
testing.cb.add('doSay', function () { console.log('test: d121d21'); } );
testing.cb.add('doSay', function () { console.log('test: wwww'); } );
testing.say();
This always seemed the easiest for me to understand... Just create a new instance of the inherited class and then loop through its variables and methods and add them to the main one.
var myPerson = new Person()
var myPerson.firstName = 'john';
var myPerson.lastName = 'smith';
var myPerson.jobTitle = 'Programmer';
var Person = function(){
//Use this to inherit classes
this._extendedClass = new Person_Job();
for(var i in this._extendedClass){
this[i] = this._extendedClass[i];
}
delete this._extendedClass;
this.firstName = '';
this.lastName = '';
}
var Person_Job = function() {
this.jobTitle = '';
}
I'm working on a fairly complex object in JS and I'm running into issues:
I have the following (abridged) code:
var LocationSelector;
LocationSelector = function(container) {
this.selectors = {
container: container,
city_name: container.find('input.city_name'),
city_id: container.find('input.city_id')
};
return this.initialize();
};
LocationSelector.prototype = {
initialize: function() {
return this.city.checkStatus();
},
city: {
status: null,
message: null,
id: null,
checkStatus: function() {
if (LocationSelector.selectors.city_name.val() && LocationSelector.selectors.city_id.val()) {
return LocationSelector.city.setStatus('success');
}
},
setStatus: function(status) {
return alert(status);
}
}
};
Two questions:
1) Inside of a sub-object function this no longer refers back to the root object. It seems I can refer back to the parent if I write out LocationSelector.object.method( args ), but that's a lot to type. Is there a way to define a shortcut back to the parent object?
2) In some situations I need to have several instances of this per page, so it's important to me that I can set the various selectors when a new object is instantiated and then refer to the instance selectors in the prototype. Is referring to the parent object (ie. LocationSelector) in sub-object methods even viable for that? How does JS know to stay with the currently active object's stored properties?
Basically, I'm trying to implement a class, and I'm totally new to JS and don't really know how to do it. So, any help or suggestions are appreciated. Thanks!
There are many things wrong with your current approach. Here is something closer to what you want, although I do not understand why LocationSelector instances have a city member.
function LocationSelector(container) {
this.selectors = {
container: container,
city_name: container.find("input.city_name"),
city_id: container.find("input.city_id")
};
this.city = new City(this);
this.city.checkStatus();
}
function City(locationSelector) {
this.status = null;
this.message = null;
this.id = null;
this.locationSelector = locationSelector;
}
City.prototype.checkStatus = function () {
if (this.locationSelector.selectors.city_name.val() && this.locationSelector.selectors.city_id.val()) {
this.setStatus("success");
}
};
City.prototype.setStatus = function () {
alert("status");
};
Things to note:
Data properties go on the instance, not the prototype. Only methods go on the prototype.
City is clearly its own class, so you should make it one. In your code, a single city is being shared between all instances of LocationSelector, since it is put on the prototype. In this code, it is assigned as an instance property, in the LocationSelector constructor.
You cannot reference LocationSelector.selectors like you do in your example. LocationSelector.selectors would be for "static" properties, which LocationSelector does not have. Instead you need to refer to the selectors property on specific instances; in this example, that instance is given by this.locationSelector.
Points 2 and 3 speak to an important fact: the "child" City instance cannot reference to properties of the "parent" LocationSelector class without having a concrete instance of it.
Here is a version of the code that makes more sense to me, removing the part where LocationSelector has a city property (which it doesn't use).
function LocationSelectors(container) {
this.city_name = container.find("input.city_name");
this.city_id = container.find("input.city_id");
}
function City(locationSelectors) {
this.locationSelector = locationSelector;
}
City.prototype.checkStatus = function () {
if (this.locationSelectors.city_name.val() && this.locationSelectors.city_id.val()) {
this.setStatus("success");
}
};
City.prototype.setStatus = function () {
alert("status");
};
function checkCityStatus(container) {
var locationSelectors = new LocationSelectors(container);
var city = new City(locationSelectors);
city.checkStatus();
}
I leave you with a link to Crockford's "Private Members in JavaScript", which talks about doing OO in JavaScript. There are other, probably better explanations out there, but at least that one will put you on the right track.
Read about Closures and I'm positive you will find what you need.
Here's is a quick example of what you are trying to accomplish:
function MyCoolObject(name){
var self_myCoolObject = this;
this.name = name;
this.popAlertWithNameInFiveSeconds = function(){
setTimeout(function(){
alert('Incorrect reference to name "this.name" returns ' + this.name);
alert('Correct reference to name "self_myCoolObject.name" returns ' + self_myCoolObject.name);
},5000)
}
}
//TO TEST
var MyObj = new MyCoolObject('MySuperCoolName')
MyObj.popAlertWithNameInFiveSeconds();
Here is a snippet of JS code that I have. Before I drop down into a click handler, I make a reference to the object (SlideShow) by calling var parent = this. Then in later nested functions, you can be sure you're calling the right scope by using parent.function()
/* Slide Show object */
function SlideShow(parentContainerId) {
this.BEGINNING = 'Beginning';
this.END = 'End of list';
this.FIRSTINDEX = 0;
this.length = jQuery(parentContainerId + ' img').length;
this.container = jQuery(parentContainerId);
this.imgs = jQuery(parentContainerId + ' img');
this.index = this.FIRSTINDEX;
this.status = 'beginning'; // beginning, end
this.init = function() {
// get it started
this.moveTo(this.FIRSTINDEX);
this.process();
// ****GET OBJECT SCOPE*****
var parent = this;
// set up click listener
this.container.find('img').click(function(e) {
var item_width = jQuery(this).width();
var x_click_ps = e.clientX - jQuery(this).offset().left;
var x_diff = x_click_ps / item_width;
console.log(this);
if (x_diff < .5) {
parent.moveByIncrement(-1)
parent.process();
} else {
parent.moveByIncrement(1);
parent.process();
}
});
}
.
.
.
}