Append to constructor - javascript

This object constructor is the definition of a person:
var Person = function( age, name ){
this.age = age;
this.namge = name;
};
this is a line of code that will give the prototype of Person an array called "active"
Person.prototype.active = [];
The reason I am adding this to the prototype, is so that there is only one active array that every person, meaning Jim in this case: var Jim = new Person() ), SHARES the exact same active array.
From that point I want to add in every newly created person into the active array.
This is how I would do it:
var Jim = new Person(age, name);
Jim.active.push( Jim );
var Tim = new Person(age, name);
Tim.active.push( Tim );
What I expected from this, is for Tim.active[0] to be Jim and for
Jim.active[1] to be Tim.
The problem is, is that I want the active.push[ self id ] to be called
when a new Person is created, without the second line doing it. My solution for this would be modifying the Person constructor too look like this:
var Person = function( age, name ){
this.age = age;
this.namge = name;
this.active.push( this );// The constructor now adds itself to the array during initiation
};
As you can see, it puhes itself into the active array. The problem is that I want my object to do exactly this, but I want the constructor to begin like the first one I provided with this.active.push appended later in the code.
How do I expect this to be solved? My thoughts were that since the active array could be initiated anytime inside the program, that the functions constructor could somehow append this.active.push() to the end of it at right after Person.prototype.active = [] is executed.
Something that may look like:
Person.prototype.active = [];
Person.prototype.append( function(){this.active.push(this)} );
The second line would morph the first object constructor to look like the second one.

Not sure why you want this approach, perhaps it can be refactored? Probably better to have some sort of PersonMediator "class" in my opinion. But, if you want to have the prototype include an active array that has conditions, then attach some way to manage those conditions to the Person object.
var Person = function( age, name ){
this.age = age;
this.namge = name;
//checks for activation flag
this.ready();
};
Person.prototype.active = [];
Person.activate = function(){ Person.prototype.activate = true; };
Person.deactivate = function(){ Person.prototype.activate = false; };
Person.prototype.activate = false;
Person.prototype.ready = function(){
//depending on activate flag, appends to active array
if( this.activate ) this.append();
};
Person.prototype.append = function(){
//array append
this.active.push(this);
};
var Jim = new Person(10,'Jim');//not added, default flag false
//activate flag for appending
Person.activate();
var Tim = new Person(20,'Tim');//now added to the active array
console.log(Jim.active);//shows only Tim
console.log(Tim.active);//shows only Tim
That said, this is how I would manage this.
var Person = function( age, name ){
this.age = age;
this.namge = name;
};
var PersonMediator = function(){
this.alive = [];
this.removed = [];
};
PersonMediator.prototype.create = function(age, name){
var person = new Person(age,name);
this.alive.push(person);
return person;
};
PersonMediator.prototype.remove = function(person){
for(var i = 0; i < this.alive.length; i++){
if( this.alive[i] === person ){
this.alive.splice(i,1);
}
}
this.removed.push(person);
};
var pm = new PersonMediator();
var Jim = pm.create(10,'Jim');
var Tim = pm.create(20,'Tim');
console.log(pm.alive);

If you simply want to store the instantiated objects to an array, you can use an inheritance pattern like so:
var myArray = [];
function SuperClass() {
myArray.push(this);
};
function Block(name) {
this.name = name;
SuperClass.apply(this, arguments);
};
function Player(name) {
this.name = name;
SuperClass.apply(this, arguments);
};
var baller = new Player('Jim');
var blocker = new Block('Joe');
console.log(myArray); // [Player, Block]
Every time a new instance of Player or Block is instantiated, it will add it to the array.

Another pattern you may consider is having a set prototype object that you can add your prototypical methods and shared data to, and then assigning it as the prototype in your constructor function
var personPrototype = {
alive: []
}
var blockPrototype = {
alive: []
}
var Person = function(x, y){
//this creates a new object with the prototype of our personPrototype object
var o = Object.create(personPrototype);
//then we can configure / add instance-specific attributes and return the object
o.x = x;
o.y = y;
return o;
}
var Block = function(x, y){
var o = Object.create(blockPrototype);
o.x = x;
o.y = y;
return o;
}
var me = new Person(1, 2);
var square = new Block(10, 20);
// > []
me.alive
By using Object.create to create a dummy object we can effectively assign it a proper prototype. Then we can add whatever instance attributes we'd like, and return that object. This means that the personPrototype will be a proper prototype. Each instance can use instance.alive or instance.alive.push directly, since it automatically walks up the prototype chains
It also allows you to easily add any new data to the prototype of all your instances. You don't need to iterate through each instance and add prototypical methods. You can just add the method to the prototype to start and it will be accessible on all instances by default.

I find the whole approach - trying to track/store [Person] instances - questionable.
But if I was in the position of being told to do so, I'd go for an approach that is build upon a
factory module. Thus being at least able of assuring "read only access" to the list of all instances
that ever got created by the factory.
A possible implementation than might look similar to the following example:
var Person = (function (global, Object, Array, Math) {
var
array_from = ((typeof Array.from == "function") && Array.from) || (function (array_prototype_slice) {
return function (listType) {
return array_prototype_slice.call(listType);
};
}(Array.prototype.slice)),
personModule = {},
personList = [],
Person = function (config) { // constructor.
var person = this;
person.name = config.name;
person.age = config.age;
person.constructor = Object;
return person;
},
isPerson = function (type) {
return (type instanceof Person);
},
createPerson = function (age, name) { // factory.
/*
- sanitizing/validation of arguments etc. should be done right here.
- create instances only if all the conditions are fulfilled.
*/
var person = new Person({
name: name,
age : age
});
personList.push(person);
return person;
}/*,
removePerson = function (type) {
if (isPerson(type)) {
// remove person from the internal list.
}
}*/
;
//personModule.remove = removePerson;
personModule.create = createPerson;
personModule.isPerson = isPerson;
personModule.all = function () { // expose internal instance list.
return array_from(personList);
};
personModule.all.size = function () {
return personList.length;
};
(function () { // make [personModule.all] enumerable.
var
parse_float = global.parseFloat,
math_floor = Math.floor
;
this.first = function () {
return (this()[0]);
};
this.last = function () {
var list;
return ((list = this())[list.length - 1]);
};
this.item = function (idx) {
return (this()[math_floor(parse_float(idx, 10))]);
};
}).call(personModule.all);
return personModule;
}((window || this), Object, Array, Math));
var Jim = Person.create("Jim", 21);
var Tim = Person.create("Tim", 19);
console.log("Jim", Jim);
console.log("Tim", Tim);
console.log("Person.isPerson(Jim) ? ", Person.isPerson(Jim));
console.log("Person.isPerson(Tim) ? ", Person.isPerson(Tim));
console.log("Person.isPerson({name: 'Tim', age: 21}) ? ", Person.isPerson({name: 'Tim', age: 21}));
console.log("Person.all.size() : ", Person.all.size());
console.log("Person.all() : ", Person.all());
console.log("Person.all.first() : ", Person.all.first());
console.log("Person.all.last() : ", Person.all.last());
console.log("(Person.all()[1] === Person.all.item(1)) ? ", (Person.all()[1] === Person.all.item(1)));
console.log("(Person.all.first() === Person.all.item(0)) ? ", (Person.all.first() === Person.all.item(0)));

Related

how to right choose javascript pattern

i create 2 objects:
var Documentos = new QuadForm();
var Cadastro = new QuadForm();
And initialize this objects with lot of options
Cadastro.initForm(options);
Documentos.initForm(options2);
then i try to separate the data managed by each object with getName method but after the second object, myObjectName variable is overrided.
var QuadForm;
QuadForm = function () {
this.getName = function () {
// search through the global object for a name that resolves to this object
for (var name in window)
if (window[name] == this) {
window[name] = this;
window[window[name]] = window[name];
myObjectName= name;
break;
}
},
this.initForm = function (parms) {
this.getName()
$.extend(this, parms);
if (window.myState) {
delete window.myState;
}
this.containerId = parms.formId;
this.getForm(parms);
this.workflowLabels('hide');
then i use window[myObjectName].totalRecords but as it changes to the latest object name off course cannot access data.
How can i manage this.
It's not a big problem to manage several instances, but your approach is impossible, cause you can't really find all possible instances and your code does definitely not what you expected to do.
For example you can define a variable on the constructor-object which holds all instances, and than you can use it in some cases:
var QuadForm = function (name) {
this.name = name;
QuadForm.instances.push(this);
this.showAllOtherInstances = function () {
QuadForm.instances.forEach(function (instance) {
if (instance !== this) {
console.log('name: ' + instance.name);
}
}.bind(this));
}
}
QuadForm.instances = [];
var foo = new QuadForm('foo');
var anotherFoo = new QuadForm('foo');
var bar = new QuadForm('bar');
var aThirdFoo = new QuadForm('foo');
foo.showAllOtherInstances();
/*
* Output:
*
* name: foo
* name: bar
* name: foo
*/

javascript object creating

I have a javascript object like this
var student = function () {
this.id = 1;
this.Name = "Shohel";
this.Roll = "04115407";
this.Session = "03-04";
this.Subject = "CSE";
};
and i have a javascript array list like this
var students = [];
now i want to push student into students, this is shown below
students.push(new student()) //no prolem
students.push(new student[id = 3]) //Problem
here second line occurs exceptions, how can i push javascript object like as c# add list, which is representing second line? thanks
You simply can't, what you can do though is accept a config as a parameter to your constructor and read it like this
var student = function (config) {
config = config || {};
this.id = config.id || 1;
this.Name = config.Name || "Shohel";
this.Roll = config.Roll || "04115407";
this.Session = config.Session || "03-04";
this.Subject = config.Subject || "CSE";
};
And call it like this
students.push(new student({id: 3}));
EDIT, PREFERRED ONE
Just as adeneo pointed out if you want to get rid of the repititive || for default values, you can use jQuery to pass them
var student = function (config) {
var defaults = {
id: 1,
Name: "Shohel",
Roll: "04115407",
Session: "03-04",
Subject: "CSE"
};
config = $.extend(defaults, config || {});
this.id = config.id;
this.Name = config.Name;
this.Roll = config.Roll;
this.Session = config.Session;
this.Subject = config.Subject;
};
Make the values that are variable parameters of the function. For example:
var Student = function (id) {
this.id = id;
// ...
};
students.push(new Student(3));
I recommend to read a JavaScript tutorial about functions:
MDN - JavaScript Guide
Eloquent JavaScript - Functions
quirksmode.org - Functions

Creating behaviour that is both like a JavaScript object literal as a function

I'd like to be able to do the following:
person('male');
person.age(34);
As you can see, I want the person object to be both a function as an object. What I mean is that doing so:
function person(gender){
this.gender = gender || 'unknown';
}
excludes me from doing so:
person.gender; // undefined
unless I create a new object from the person function, like so:
var me = new person();
me.gender; // 'unknown'
which I do not want, as I still want to be able to set other properties, like so:
person.age = 34;
Is this even possible? Of am I trying to accomplish the impossible?
To clarify: I don't want to need to create instances. The key is that I just want to be able to both call the function person as call a function.property().
EDIT: Perhaps I've confused many (including myself) by using a wrong example. A simpler question would be: How would I be able to call a function and a 'property' of that function, like so:
car();
car.start();
both at the same time without the need to create an instance of car?
We might be able to give a better answer if you would explain the motivation behind what you want to do. However, yes, what you describe is possible. In JavaScript, functions are just another type of object. That means that you could define a function, then set properties on it like so:
var person = function () {
return "Hi, I am a person!";
};
person.species = "human";
person.foo = 42;
console.log( person() ); // prints: Hi, I am a person!
console.log( person.species ): // prints: human
console.log( person.foo ); // prints: 42
Or working from your second example:
var car = function () {
return {
model: "Focus",
make: "Ford",
year: 2010
};
};
car.start = function () {
return "Vroom!";
};
console.log ( car.make ); // prints: Ford
console.log ( car.start() ); // prints Vroom!
By the way, this is not the same as a function that returns an object. That might look like this:
var person = function () {
return {
species: "human",
name: "Bob",
age: 27
};
};
var bob = person();
bob.occupation = "cubicle drone";
console.log( bob.name ); // prints: Bob
console.log( bob.occupation ); // prints: cubicle drone
console.log( person.occupation ); // prints: undefined
console.log( bob == person ); // prints: false
Variables bob and person both hold objects, but not the same object, not even the same type of object.
Instead of using this inside your function, which refers to the Object instance created when running this function as a constructor by invoking it with the new operator you can use arguments.callee to refer to the function itself
function person(gender){
arguments.callee.gender = gender || 'unknown';
}
If you want to ensure that a function is always used as a constructor even if invoked without the new operator, you can do it by implicitly invoking it.
function person(gender){
if(!(this instanceof person)){ return new person(gender); }
this.gender = gender || 'unknown';
}
In all other regards functions can do anything regular objects can, that's why they are often described as "first class objects".
You can simply assign them properties like objects, or methods like
function car(){
}
car.start = function(){
}
You can also let them inherit methods from a function using the __proto__ property.
function bird(){}
function platypus(){}
bird.layEggs = function(){};
platypus.__proto__ = bird;
platypus.layEggs() // now you know
a small example using the instanceof way that #Winchestro is using
on jsfiddle:http://jsfiddle.net/47EEr/
'use strict';
function person(gender, age, name) {
if (!(this instanceof person)) {
return new person(gender, age, name);
}
var props = { gender: '', age: 0, name: '' }, fnCreateProp = function(prop, value) {
Object.defineProperty(this, prop, {
get: function() {
return props[prop];
},
set: function(val) {
console.log('setting prop ' + prop + ' to ' + val);
props[prop] = val;
}
});
}.bind(this);
for (var prop in props) {
if (!props.hasOwnProperty(prop)) {
continue;
}
fnCreateProp(prop, props[prop]);
}
this.gender = gender;
this.age = age;
this.name = name;
this.toString = function(element) {
var str = '[Person=(';
for (var prop in props) {
if (!props.hasOwnProperty(prop)) {
continue;
}
if (str.indexOf('(') !== str.length-1) {
str += ',';
}
str += prop +': ' + props[prop];
}
str += ')]';
if (typeof element !== 'undefined') {
var el = document.getElementById(element);
if (el) {
el.innerHTML = str;
}
}
return str;
};
this.birthday = function() {
this.age++;
};
}
var p1 = person('male', 33, 'male 1');
var p2 = new person();
p2.age = 32; p2.gender = 'female'; p2.name = 'female 1';
p1.toString('person1');
p2.toString('person2');
p2.birthday();
p2.toString('person2birthday');
You can also do:
function person(gender){
return {
gender: gender || 'unknown'
};
}
var p = person('male');
p.age = 34;
p.name = 'John';
etc...
No "new" is used and you can still re-use the person function.
Your edit:
EDIT: Perhaps I've confused many (including myself) by using a wrong example. A simpler question would be: How would I be able to
call a function and a 'property' of that function, like so:
car();
car.start();
both at the same time without the need to create an instance of car?
Is this what you are looking for?
function car() {
console.log("car called");
car.start = function() {
console.log("car.start called");
}
}
car();
car.start();
/**** output:****
car called
car.start called
*/

stringify javascript function

I am in the final stages of a game development and i have a bunch of objects like this;
roomBedroom = function () {
this.title = "Bedroom";
this.description = "I'm in a bedroom";
this.noun = "bed";
this.entities = new Array();
}
var bedroom = new roomBedroom();
What I want to do now is place all of my game objects into an array;
var savedGameObjects = {};
savedGameObjects['bedroom'] = bedroom;
var jsonGame = JSON.stringify(savedGameObjects);
The plan is to then save the savedGameObjects array and then recall it when the user loads the game again.
If I replace savedGameObjects['bedroom'] = bedroom; with savedGameObjects['bed'] = 'slappy'; it works but not when I have the object.
I really need to save the objects in their current state. I'd rather not go through each object saving key pieces of information one by one.
This feels like a bit of a hack, but its the best I can come up with right now
Your serialization/deserializtion utility
This is going to attach obj.constructor.name to obj.__prototype before serialization. Upon deserializing, the prototype will be put back in place.
(function(global) {
function serialize(obj) {
obj.__prototype = obj.constructor.name;
return JSON.stringify(obj);
};
function deserialize(json) {
var obj = JSON.parse(json);
obj.__proto__ = global[obj.__prototype].prototype;
return obj;
}
global.serialize = serialize;
global.deserialize = deserialize;
})(window);
A sample "class"
(function(global) {
function Foo() {
this.a = "a";
this.b = "b";
}
Foo.prototype.hello = function() {
console.log("hello");
}
global.Foo = Foo;
})(window);
Let's try it out
var foo = new Foo();
var json = serialize(foo);
console.log(json);
var newFoo = deserialize(json);
console.log('a', newFoo.a); // a
console.log('b', newFoo.b); // b
newFoo.hello(); // hello
Watch out for some gotchas
If you use an expression to define your "class", you will have a nameless constructor
var Foo = function() {};
var foo = new Foo();
foo.constructor.name; // ""
As opposed to a named function
function Foo() {}
var foo = new Foo();
foo.constructor.name; // Foo
In order for serialize and deserialize to work, you will need to use named functions
Another gotcha
The deserialize method expects your "classes" to exist on the in the same namespace (window in this case). You could encapsulate your game object classes in another way, just make sure that you reconfigure the deserialize method so that it can find the prototypes as needed.
Making this better
Instead of attaching serialize to the global window, you could have serialize live on (e.g.) the GameObject.prototype then your individual classes could inherit from GameObject. Serializing an object would then be as simple as
var json = foo.serialize();
// {"a":"a","b":"b","__prototype":"Foo"}
You could then define deserialize as GameObject.deserialize and restoring foo would be
var foo = GameObject.deserialize(json);
An alternative solution
Instead of implementing a custom serializer and deserializer, you could make very clever use of the Factory Method Pattern.
This might be a little verbose, but it does give you individual control over how a game object should be deserialized/restored.
var savedData = // your normal JSON here
var player = Player.create(savedData.player);
var items = [];
for (var i=0, i<savedData.items.length; i++) {
items.push(Item.create(savedData.items[i]));
}
var map = Map.create(savedData.map);
This was a pretty interesting problem and I'm sure you're not the first to encounter it. I'm really curious to see what other people come up with.
If I run the following code in a browser there is no problem getting the JSON string of the bedroom object, not sure what the problem is.
Note that JSON is data and bedroom is an object, bedroom may have behaviour like turnOffLight() that JSON doesn't have.
roomBedroom = function () {
this.title = "Bedroom";
this.description = "I'm in a bedroom";
this.noun = "bed";
this.entities = new Array();
}
var bedroom = new roomBedroom();
var savedGameObjects = {};
savedGameObjects['bedroom'] = bedroom;
//logs {"bedroom":{"title":"Bedroom","description":
// "I'm in abedroom","noun":"bed","entities":[]}}
console.log(JSON.stringify(savedGameObjects));
So if you want to re create object instances from JSON data then you can change your constructor:
roomBedroom = function (args) {
//following fails fast and loud, you could silently
//fail by setting args to {}
if(typeof args!=="object")
throw new Error("Have to create roomBedroom by passing an object");
//or do args={} to silently fail
this.title = args.title||"Bedroom";
this.description = args.description||"I'm in a bedroom";
this.noun = args.noun||"bed";
//if entities are objects with behavior
// you have to re create them here passing the JSON data
// as I've done with roomBedroom
this.entities = args.entities||new Array();
}
var jsonString='{"bedroom":{"title":"Bedroom",'+
'"description":"I\'m in a bedroom",'+
'"noun":"bed","entities":[]}}';
var bedroom = new roomBedroom({});
bedroom.entities.push({hi:"there"});
bedroom.title="Master Bedroom";
//serialize bedroom to a json string
var jsonString = JSON.stringify(bedroom);
//create a roomBedroom instance named br2 using
// the serialized string
var br2=new roomBedroom(JSON.parse(jsonString));
//compare if they are the same
console.log(JSON.stringify(bedroom)===JSON.stringify(br2));//true
I have an approach that might work for you. You can see it in action on JSFiddle.
The main point is to use the reviver parameter to JSON.parse to reconstruct your object when it's parsed.
I do this with a general-purpose reviver that can be configured for multiple different types, although here the only one used is the RoomBedroom constructor. This implementation assumes that you have simple copy constructors that create new objects using a reference to an existing one. (For other, more sophisticated possibilities, see an answer to another question I gave in February.) To make it easy to have a copy constructor, I have one more function that accepts a very simple constructor function and a set of default values and builds a copy constructor function for you.
var MultiReviver = function(types) {
return function(key, value) {
var type;
for (var i = 0; i < types.length; i++) {
type = types[i];
if (type.test(value)) {
return new type.constructor(value);
}
}
return value;
};
};
var makeCloningConstructor = (function() {
var clone = function(obj) {return JSON.parse(JSON.stringify(obj));};
var F = function() {};
return function(Constructor, defaults) {
var fn = function(obj) {
Constructor.call(this);
var self = this;
var config = obj || {};
Object.keys(defaults).forEach(function(key) {
self[key] = clone(defaults[key]);
});
Object.keys(config).forEach(function(key) {
self[key] = clone(config[key]);
});
};
F.prototype = Constructor.prototype;
fn.prototype = new F();
fn.constructor = Constructor;
return fn;
};
})();
// Note: capitalize constructor functions
var RoomBedroom = makeCloningConstructor(function RoomBedroom() {}, {
title: "Bedroom",
description: "I'm in a bedroom",
noun: "bed",
entities: [] // Note: use `[]` instead of `new Array()`.
});
RoomBedroom.prototype.toggleLight = function() {
this.lightOn = !this.lightOn;
};
RoomBedroom.prototype.checkLights = function() {
return "light is " + (this.lightOn ? "on" : "off");
};
var bedroom = new RoomBedroom();
bedroom.windowCount = 3; // add new property
bedroom.noun = "king-sized bed"; // adjust property
bedroom.toggleLight(); // create new propery, use prototype function
console.log(bedroom.checkLights());
var savedGameObjects = {};
savedGameObjects['bedroom'] = bedroom;
var jsonGame = JSON.stringify(savedGameObjects);
var reviver = new MultiReviver([{
constructor: RoomBedroom,
test: function(obj) {
var toString = Object.prototype.toString, str = "[object String]",
arr = "[object Array]";
return toString.call(obj.title) == str &&
toString.call(obj.description) == str &&
toString.call(obj.noun) == str &&
toString.call(obj.entities) == arr;
}
}]);
var retrievedGameObjects = JSON.parse(jsonGame, reviver);
// data comes back intact
console.log(JSON.stringify(retrievedGameObjects, null, 4));
// constructor is as expected
console.log("Constructor: " + retrievedGameObjects.bedroom.constructor.name);
// prototype functions work
console.log(retrievedGameObjects.bedroom.checkLights());
I don't know if it's precisely what you were looking for, but I think it's at least an interesting approach.
the faster route
It is better — from an optimisation point of view — to do as Adeneo states, which is power each of your Game Objects by an exportable simple object i.e:
roomBedroom = function(){
this.data = {};
this.data.title = 'Bedroom'
/// and so on...
}
These can then be easily stored and re-imported just by JSON.Stringifying and overwriting the data property. For example, you could set-up the system that Maček mentions (+1) which is to give each of your game objects serialize and deserialize functions:
roomBedroom.prototype.serialize = function(){
return JSON.stringify( this.data );
};
roomBedroom.prototype.deserialize = function( jstr ){
this.data = JSON.parse(jstr);
};
the quicker way
However, you can make a simple addition to what you already have using the following:
First enhance your Game Objects with an objectName property. This is because constructor.name and function.name are unreliable and do strange things the further back in time you go, far better to use a string you have set in stone.
var roomBedroom = function ( title ) {
this.objectName = "roomBedroom";
this.title = title;
this.description = "I'm in a bedroom";
this.noun = "bed";
this.entities = new Array();
};
Then the additional code to help with storage:
var storage = {};
/// add your supported constructors to this list, there are more programmatic
/// ways to get at the constructor but it's better to be explicit.
storage.constructors = {
'roomBedroom' : roomBedroom
};
/// take an instance and convert to simple object
storage.to = function( obj ){
if ( obj.toStorage ) {
return obj.toStorage();
}
else {
var keep = {};
for ( var i in obj ) {
if ( obj.hasOwnProperty(i) && !obj[i].call ) {
keep[i] = obj[i];
}
}
return keep;
}
}
/// take simple object and convert to an instance of constructor
storage.from = function( obj ){
var n = obj && obj.objectName, c = storage.constructors[n];
if ( n && c ) {
if ( c.fromStorage ) {
return c.fromStorage( obj );
}
else {
var inst = new c();
for ( var i in obj ) {
if ( obj.hasOwnProperty(i) ) {
inst[i] = obj[i];
}
}
return inst;
}
}
else {
throw new Error('`' + n + '` undefined as storage constructor');
}
}
Once you have that you can use it like so:
var savedGameObjects = {};
savedGameObjects['bedroom'] = storage.to(new roomBedroom("bedroom"));
savedGameObjects['bedroom2'] = storage.to(new roomBedroom("bedroom2"));
var jsonGame = JSON.stringify(savedGameObjects);
console.log(jsonGame);
savedGameObjects = JSON.parse(jsonGame);
for( var i in savedGameObjects ) {
savedGameObjects[i] = storage.from(savedGameObjects[i]);
console.log(savedGameObjects[i]);
}
extras
You can also be specific about the way objects get stored/unstored by supplying toStorage and fromStorage methods on your constructed instances and constructors respectively. For example, you could use the following if you only wanted to store titles of roomBedrooms. Obviously this is an unrealistic use-case, you'd more often use this to avoid storing cached or computed sub-objects and properties.
roomBedroom.prototype.toStorage = function( obj ){
var ret = {};
ret.title = obj.title;
return ret;
};
roomBedroom.fromStorage = function( obj ){
var inst = new roomBedroom();
inst.title = obj.title;
return inst;
};
The above also means you can take advantage of improving your Game Object construction by providing parameters, rather than iterating over properties which can be slow and error-prone.
roomBedroom.fromStorage = function( obj ){
return new roomBedroom( obj.title );
};
Or even:
roomBedroom.fromStorage = function( obj ){
return new roomBedroom( obj ); // <-- the constructor processes the import.
};
fiddle
http://jsfiddle.net/XTUdp/
disclaimer
The above code relies on the existence of hasOwnProperty which is not present cross-browser yet, a polyfill should be used until it is... or, if you aren't doing anything complicated with prototype inheritance you don't need to worry and can remove it from the code.
you can declare a big variable like
var world = {};
and each small variable declare as
var bedroom = world.bed = (world.bed || new roomBedroom());
remember never change bedroom to another object, i think this will work fine, but looks too long winded

Class inheritance and private variables in JS

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);
}

Categories

Resources