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();
}
});
}
.
.
.
}
Related
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);
I've been building a small JS framework for use at my job, and I'd like to employ Douglas Crockford's prototypical inheritance patterns. I think I get the general idea of how the prototype object works, but what isn't clear is the way in which I would use this pattern beyond the simplest example.
I'll flesh it out to the point that I understand it.
(function () {
'use strict';
var Vehicles = {};
Vehicles.Vehicle = function () {
this.go = function () {
//go forwards
};
this.stop = function () {
//stop
};
};
Vehicles.Airplane = Object.create(Vehicles.Vehicle());
}());
So now my Vehicles.Airplane object can go() and stop(), but I want more. I want to add takeOff() and land() methods to this object. I could just use ugly dot notation afterwards:
Vehicles.Airplane.takeOff = function () {
//take off stuff
}
But that seems wrong, especially if I were to add many methods or properties. The question asked at here seems to be very similar to mine, but the answer doesn't quite ring true for me. The answer suggests that I should build an object literal before using Object.create, and that I should pass that object literal into the create method. In the example code given, however, it looks like their new object inherits nothing at all now.
What I'm hoping for is some syntax similar to:
Vehicles.Airplane = Object.create(Vehicles.Vehicle({
this.takeOff = function () {
//takeOff stuff
};
this.land = function () {
//land stuff
};
}));
I know this syntax will break terribly with Object.create right now, because of course I'm passing Vehicle.Vehicle a function rather than an object literal. That's beside the point. I'm wondering in what way I should build new properties into an object that inherits from another without having to list them out one at a time with dot notation after the fact.
EDIT:
Bergi, after some anguished thought on the topic, I think I really want to go with what you described as the "Classical Pattern". Here is my first stab at it (now with actual code snippets rather than mocked up hypotheticals - You even get to see my crappy method stubs):
CS.Button = function (o) {
o = o || {};
function init(self) {
self.domNode = dce('a');
self.text = o.text || '';
self.displayType = 'inline-block';
self.disabled = o.disabled || false;
self.domNode.appendChild(ctn(self.text));
if (o.handler) {
self.addListener('click', function () {
o.handler(self);
});
}
}
this.setText = function (newText) {
if (this.domNode.firstChild) {
this.domNode.removeChild(this.domNode.firstChild);
}
this.domNode.appendChild(ctn(newText));
};
init(this);
};
CS.Button.prototype = Object.create(CS.Displayable.prototype, {
constructor: {value: CS.Button, configurable: true}
});
CS.Displayable = function (o) { // o = CS Object
o = o || {};
var f = Object.create(new CS.Element(o));
function init(self) {
if (!self.domAnchor) {
self.domAnchor = self.domNode;
}
if (self.renderTo) {
self.renderTo.appendChild(self.domAnchor);
}
}
//Public Methods
this.addClass = function (newClass) {
if (typeof newClass === 'string') {
this.domNode.className += ' ' + newClass;
}
};
this.addListener = function (event, func, capture) {
if (this.domNode.addEventListener) {
this.domNode.addEventListener(event, func, capture);
} else if (this.domNode.attachEvent) {
this.domNode.attachEvent('on' + event, func);
}
};
this.blur = function () {
this.domNode.blur();
};
this.disable = function () {
this.disabled = true;
};
this.enable = function () {
this.disabled = false;
};
this.focus = function () {
this.domNode.focus();
};
this.getHeight = function () {
return this.domNode.offsetHeight;
};
this.getWidth = function () {
return this.domNode.offsetWidth;
};
this.hide = function () {
this.domNode.style.display = 'none';
};
this.isDisabled = function () {
return this.disabled;
};
this.removeClass = function (classToRemove) {
var classArray = this.domNode.className.split(' ');
classArray.splice(classArray.indexOf(classToRemove), 1);
this.domNode.className = classArray.join(' ');
};
this.removeListener = function () {
//Remove DOM element listener
};
this.show = function () {
this.domNode.style.display = this.displayType;
};
init(this);
};
CS.Displayable.prototype = Object.create(CS.Element.prototype, {
constructor: {value: CS.Displayable, configurable: true}
});
I should be quite clear and say that it's not quite working yet, but mostly I'd like your opinion on whether I'm even on the right track. You mentioned "instance-specific properties and methods" in a comment in your example. Does that mean that my this.setText method and others are wrongly placed, and won't be available to descendant items on the prototype chain?
Also, when used, it seems that the order of declaration now matters (I can't access CS.Displayable.prototype, because (I think) CS.Button is listed first, and CS.Displayable is undefined at the time that I'm trying to reference it). Is that something I'll just have to man up and deal with (put things in order of ancestry in the code rather than my OCD alphabetical order) or is there something I'm overlooking there as well?
Vehicles.Airplane = Object.create(Vehicles.Vehicle());
That line is wrong. You seem to want to use new Vehicles.Vehicle - never call a constructor without new!
Still, I'm not sure which pattern you want to use. Two are coming to my mind:
Classical Pattern
You are using constructor functions just as in standard JS. Inheritance is done by inheriting the prototype objects from each other, and applying the parent constructor on child instances. Your code should then look like this:
Vehicles.Vehicle = function () {
// instance-specific properties and methods,
// initialising
}
Vehicles.Vehicle.prototype.go = function () {
//go forwards
};
Vehicles.Vehicle.prototype.stop = function () {
//stop
};
Vehicles.Airplane = function() {
// Vehicles.Vehicle.apply(this, arguments);
// not needed here as "Vehicle" is empty
// maybe airplane-spefic instance initialisation
}
Vehicles.Airplane.prototype = Object.create(Vehicles.Vehicle.prototype, {
constructor: {value:Vehicles.Airplane, configurable:true}
}); // inheriting from Vehicle prototype, and overwriting constructor property
Vehicles.Airplane.prototype.takeOff = function () {
//take off stuff
};
// usage:
var airplane = new Vehicles.Airplace(params);
Pure Prototypical Pattern
You are using plain objects instead of constructor functions - no initialisation. To create instances, and to set up inheritance, only Object.create is used. It is like having only the prototype objects, and empty constructors. instancof does not work here. The code would look like this:
Vehicles.Vehicle = {
go: function () {
//go forwards
},
stop: function () {
//stop
}
}; // just an object literal
Vehicles.Airplane = Object.create(Vehicles.Vehicle); // a new object inheriting the go & stop methods
Vehicles.Airplane.takeOff = function () {
//take off stuff
};
// usage:
var airplane = Object.create(Vehicles.Airplane);
airplane.prop = params; // maybe also an "init" function, but that seems weird to me
You got Object.create wrong. The first argument should be an object (maybe that's why people suggested you pass a literal).
In your first example, you're actually passing undefined:
Vehicles.Airplane = Object.create(Vehicles.Vehicle()); // the function call will
// return undefined
The following would work, but it's not very Crockford-ish:
Vehicles.Airplane = Object.create(new Vehicles.Vehicle());
The way I believe Crockford would do it (or, at least, wouldn't complain of):
var Vehicles = {};
Vehicles.Vehicle = {
go : function() {
// go stuff
},
stop : function() {
// go stuff
}
};
Vehicles.Airplane = Object.create(Vehicles.Vehicle, {
takeOff : {
value : function() {
// take-off stuff
}
},
land : {
value: function() {
// land stuff
}
}
});
Note that Vehicles.Vehicle is just a literal, which will be used as the prototype for other objects. When we call Object.create, we pass Vehicles.Vehicle as the prototype, and takeOff and land will be own properties of Vehicles.Airplane. You may then call Object.create again, passing Vehicles.Airplane as the prototype, if you want to create e.g. a Boeing.
The own properties passed as the second parameter are packed in an object that contains a representation of their property descriptors. The outer keys are the names of your properties/methods, and each one points to another object containing the actual implementation as the value. You may also include other keys like enumerable; if you don't they'll take the default values. You can read more about descriptors on the MDN page about Object.defineProperty.
current broken code: http://jsfiddle.net/9F52n/2/
What I'm trying to do: Learn how to define an object/function that behaves like a class, and be able to define subclasses, both static , and instantiatable (singleton in my example below).
Currently, my code below doesn't work all that well. but, if the instantiatable class sand the static class were removed, you'll see that I have the very basics of class creation down.
So, I guess my question is: what is the proper / most semantic way to define nested clases (singleton or otherwise) with the way I've defined TBR? (function(){...})(window)
var TBR = (function() {
// define local copy of taco bell run
var TBR = function() {
return new TBR.fn.init();
},
message = "hello world!";
TBR.fn = TBR.prototype = {
constructor: TBR,
init: function() {
console.log("From TBR Constructor: " + message);
}
}
var InstantiatableClass = function() {
return new TBR.InstantiatableClass, fn.init();
}
InstantiatableClass.fn =InstantiatableClass.prototype = {
constructor: TBR.InstantiatableClass,
init: function() {
console.log("from InstantiatableClass: " + message);
}
}
this.staticClass = function() {
var subMessage = "little world";
init = function() {
console.log("from staticClass: " + subMessage);
}
}
// expose TBR to the window object
window.TBR = TBR;
})(window);
var InstantiatableClass = function() {
return new TBR.InstantiatableClass, fn.init();
}
InstantiatableClass.fn =InstantiatableClass.prototype ...
This does not work. Your InstantiatableClass local variable returns objects, the prototype will not get applied to them. Also, TBR.InstantiatableClass is defined nowhere. If that is what you wanted, you'd need to use
function InstantiatableClass() {
// common constructor things
}
TBR.InstantiatableClass = InstantiatableClass; // assign a "static" property
Also, you should not [need to] overwrite the prototypes. Sure, the only difference is that constructor is enumerable now (as far as it is not forgotten), but the following would be much cleaner:
InstantiatableClass.fn = InstantiatableClass.prototype; // shortcut
InstantiatableClass.fn.init = function() { … };
Oh, you want something that works like jQuery. Imho you should not make the constructor (init) a property of the prototype - that is just very odd and I can't see a reason to do so. I'd suggest this code:
window.TBR = (function() {
function TbrConstructor() {
…
}
function InstantiableConstructor() {
…
}
// Now, the creator functions:
function TBR() { return new TbrConstructor; }
function Instantiable() { return new InstantiableConstructor; }
// Now, overwrite the "prototype" properties - this is needed for
// (new TbrConstructor) instanceof TBR === true
// and also create those fn shortcuts
TBR.fn = TBR.prototype = TbrConstructor.prototype;
Instantiable.fn = Instantiable.prototype = InstantiableConstructor.prototype;
// we might overwrite the "constructor" properties like
TBR.fn.constructor = TBR;
// but I don't see much sense in that - they also have no semantic value
// At last, make Instantiable a property on the TBR function object:
TBR.Instantiable = Instantiable;
// and then only
return TBR;
})();
// Usage
TBR(); // returns a TbrConstructor instance
TBR.Instantiable(); // returns a InstantiableConstructor instance
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 = '';
}
Just for the kicks i am trying to create a simple data object in javascript. Here is the code.
var roverObject = function(){
var newRover = {};
var name;
var xCord;
var ycord;
var direction;
newRover.setName = function(newName) {
name = newName;
};
newRover.getName = function() {
return name;
};
newRover.setDirection = function(newDirection) {
direction = newDirection;
};
newRover.getDirection = function() {
return direction;
};
newRover.setXCord = function(newXCord) {
xCord = newXCord;
};
newRover.getXCord = function() {
return xCord;
};
newRover.setYCord = function(newYCord) {
yCord = newYCord;
};
newRover.getYCord = function() {
return yCord;
};
newRover.where = function(){
return "Rover :: "+ name +" is at Location("+xCord+","+yCord+") pointing to "+direction;
};
return newRover;
};
rover1 = new roverObject();
rover2 = new roverObject();
rover1.setName("Mars Rover");
rover1.setDirection("NORTH");
rover1.setXCord(2);
rover1.setYCord(2);
console.log(rover1.where());
console.log(rover1);
rover2.setName("Moon Rover");
rover2.setDirection("SOUTH");
rover2.setXCord(1);
rover2.setYCord(1);
console.log(rover2.where());
console.log(rover2);
There are few questions that I have around this creation.
I want to create an object where the properties/attributes of object are private and not visible to world. Am I successful in doing that? Can I really not access the object attributes?
Is there a better way to create this kind of object?
If I want to inherit this object, I should do a newObject.prototype = roverObjectwill that work? And will that make sense most of all.
Finally I have a wierd problem. Notice the last method of objet "where" which returns a concatenated string. Here I tried following code instead.
newRover.where = function(){
return "Rover :: "+ name +" is at Location("+xCord+","+yCord+") pointing to "+direction;
}();
and then did a following console.log
console.log(rover1.where);
console.log(rover2.where);
It threw following error for me:
cannot access optimized closure
Why would it say that? What am I doing wrong?
Thanks for all the help. Any review comments would be appreciated too!
Cheers
Am I successful in doing that? Can I really not access the object attributes?
Indeed. You don't have object attributes, you have local variables in the roverObject function. Local variables can't be accessed from outside, only from the functions inside the roverObject function that have a closure over them.
That you are calling roverObject as a constructor, with new roverObject, is irrelevant, as you are returning a different object from the function. Saying var rover1= roverObject() without the new would do exactly the same thing. Notably the object returned by [new] roverObject is a plain Object as you created it from {}; rover1 instanceof roverObject is false.
If you wanted instanceof to work, you would have to call with new, and use this instead of newRover in the constructor function.
If I want to inherit this object, I should do a newObject.prototype = roverObject will that work? And will that make sense most of all.
No. You currently have no allowance for prototyping. You are using a separate copy of each method for each instance of the roverObject. You can do certainly objects this way but it's a different approach than prototyping. If you wanted to make something like a subclass of roverObject in the arrangement you have now, you'd say something like:
function AdvancedRover() {
var rover= new roverObject();
rover.doResearch= function() {
return rover.where()+' and is doing advanced research';
};
return rover;
}
Note since the ‘private’ local variables in the base class constructor really are private, even the subclass cannot get at them. There's no ‘protected’.
newRover.where = function(){ ... }();
What's that trying to do? I can't get the error you do; all the above does is assigns the string with the location to where (before the setter methods have been called, so it's full of undefineds).
Is there a better way to create this kind of object?
Maybe. see this question for a discussion of class/instance strategies in JavaScript.
Q1: you can create 'private' members in javascript 'classes'. In javascript, privacy is not determined by any access specifier. Instead, access needs to be specifically instrumented. Example:
function MyClass() {
this.val = 100; // public;
var privateVal = 200;
function getVal() { return this.val; } // private method;
this.getPrivateVal = function() { // public method, accessor to private variable
return privateVal;
}
}
Object scope in javascript is governed by a queer concept called closures. AFAIK, there is no parallel concept in any other popular launguage like C+/Java etc.
While I understand what closures are, I cannot put it in words. Perhaps a demonstration will help you:
function closureDemo() {
var done=false;
function setDone() { done=true; }
doLater(setDone);
}
function doLater(func) { setTimeout(func,1000); }
closureDemo();
now, while setDone is called from within doLater, it can still access done in closureDemo, even though done is not in scope (in the conventional procedural sense).
I think you will understand more when you read this.
Q2: I can only say what I do; I don't know if it is better or not. If I wrote your code, it would look like this:
function RoverObject() {
var newRover = {}; // privates
var name;
var xCord;
var ycord;
var direction;
this.setName = function(newName) {
name = newName;
};
this.getName = function() {
return name;
};
this.setDirection = function(newDirection) {
direction = newDirection;
};
// and so on...
this.where = function(){
return "Rover :: "+ name +" is at Location("+xCord+","+yCord+") pointing to "+direction;
};
}
var rover1 = new RoverObject();
Points to note:
capitalization of "class name"'s first letter
use of this instead of roverObject
this function is a pure constructor. it returns nothing.
Q3: if you want to do inheritance, then my method (use of this) will not work. Instead, the public methods should be a part of the prototype of RoverObject. Read this. Excellent material.
Hope that helps.
EDIT: There is a problem with the way your code is doing work. Problems:
your function does not do what its name suggests. Its name had better be createRoverObject, because that's exactly what it is doing. It is not working like a class constructor
the methods supported by your class are part of the object, but the data members are not. While this may work (and it is not, as your console.log() problem suggests), it is not a good way to implement a class in javascript. The problem here is of closures. Again, i'm unable to articulate what the problem specifically is, but I can smell it.
With regards to 4. - you are trying to log the function, not the result of calling the function. Should be console.log(rover1.where()); My guess firebug(I assume it's firebug's console.log) does not like to log function definitions.
EDIT Oh I get it, you are actually executing the where funcion when you assign rover.where. Are you trying to get what looks like a property to actually be a function? If that's the case it won't work. It will have to be a function if you want it to be evaluated when it's called.
What happens in you case where gets executed in the constructor function. At that point you are still creating the roverObject closure and hence it's too early to access it's private variables.
This is just addressing point 1 of your post.
Here's a good article on javascript private members and more:
Private Members in JavaScript
Defining your object like this gives you private members.
function RolloverObject() {
var name;
var xCord;
var ycord;
var direction;
this.setName = function(newName) { name = newName; };
this.getName = function() { return name; };
this.setDirection = function(newDirection) { direction = newDirection; };
this.getDirection = function() { return direction; };
this.setXCord = function(newXCord) { xCord = newXCord; };
this.getXCord = function() { return xCord; };
this.setYCord = function(newYCord) { yCord = newYCord; };
this.getYCord = function() { return yCord; };
this.where = function() {
return "Rover :: " + name + " is at Location(" + xCord + "," + yCord + ") pointing to " + direction;
};
}
var rolloverObject = new RolloverObject();