Casting plain objects to class instances in javascript - javascript

function Person() {
var self = this;
self.personName="";
self.animals=[];
}
function Animal(){
var self=this;
self.animalName="";
self.run=function(meters){
.....
}
}
Server response:
[{personName:John,animals:[{animalName:cheetah},{animalName:giraffe}]} , {personName:Smith,animals:[{animalName:cat},{animalName:dog}]} ]
I'm getting Person array from server. I want to cast generic Person array to typed Person array. So I can use
persons[0].Animals[2].Run();
I founded Javascript's
Object.create(Person,person1);
But I want cross-browser version of it with array support
ObjectArray.create(Person,persons);
or
Object.create(Person[],persons);

Creating an object in JavaScript requires the invocation of its constructor. So, at first you will need to find the correct arguments, which may not always be just properties. After that, you can reassign all public properties from the JSON-parsed object to the created instances.
A general solution would be that every constructor accepts any objects that look like instances (including real instances) and clones them. All the internal logic needed to create proper instances will be located in the right place then.
Or even better than overloading the constructor might be to create a static method on your class that takes objects and creates instances from them:
Person.fromJSON = function(obj) {
// custom code, as appropriate for Person instances
// might invoke `new Person`
return …;
};
Your case is very simple, as you don't have any arguments and only public properties. To change {personName:John,animals:[]} to an object instance, use this:
var personLiteral = ... // JSON.parse("...");
var personInstance = new Person();
for (var prop in personLiteral)
personInstance[prop] = personLiteral[prop];
You can also use Object.assign functionality (or e.g. jQuery.extend pre-ES6) for this:
var personInstance = Object.assign(new Person(), personLiteral);
The creation of the Animal instances works analogous.
As JSON does not transport any information about the classes, you must know the structure before. In your case it will be:
var persons = JSON.parse(serverResponse);
for (var i=0; i<persons.length; i++) {
persons[i] = $.extend(new Person, persons[i]);
for (var j=0; j<persons[i].animals; j++) {
persons[i].animals[j] = $.extend(new Animal, persons[i].animals[j]);
}
}
Btw, your run methods seems likely to be added on the Animal.prototype object instead of each instance.

It seems like you have classes that have some prototype methods and you'd just like to be able to make your objects use those methods. http://jsfiddle.net/6CrQL/3/
function Person() {}
Person.prototype.speak = function() {
console.log("I am " + this.personName);
};
Person.prototype.runAnimals = function() {
this.animals.each(function(animal){
animal.run();
})
};
function Animal() {}
Animal.prototype.run = function() {
console.log("My Animal " + this.animalName+ " is running");
}
var untypedPersons = [{personName:"John",animals:[{animalName:"cheetah"},{animalName:"giraffe"}]} , {personName:"Smith",animals:[{animalName:"cat"},{animalName:"dog"}]} ];
function fromArray(arr, constructor) {
return arr.map(function(obj){
var typed = Object.create(constructor.prototype);
// Now copy properties from the given object
for (var prop in obj) {
typed[prop] = obj[prop];
}
return typed;
});
}
var persons = fromArray(untypedPersons, Person);
// Attach prototype to each animals list in person
persons.each(function(person){
person.animals = fromArray(person.animals, Animal);
});
persons.each(function(person){
person.speak();
person.runAnimals();
});
​This could all be a lot easier (and we could avoid all the copying) if everybody supported the __proto__ property http://jsfiddle.net/6CrQL/2/
persons.each(function(person){
person.__proto__ = Person.prototype;
person.animals.each(function(animal){
animal.__proto__ = Animal.prototype;
});
});
persons.each(function(person){
person.speak();
person.runAnimals();
});​

First of all: In JavaScript you don't have classes like in C++, Java or C#. So you cannot really have a typed array.
What you are doing should basically work for variables, but not for functions. So you would have to add the functions first. Have a look at the following code to get an idea.
<script type="text/javascript">
function Person() {
var self = this;
self.personName="";
self.animals=[];
}
function Animal(){
var self=this;
self.animalName="";
self.run=function(meters){
7/... do something
}
}
var persons = [{personName:"John",animals:[{animalName:"cheetah"},{animalName:"giraffe"}]} , {personName:"Smith",animals:[{animalName:"cat"},{animalName:"dog"}]} ];
//use this to assign run-function
var a = new Animal();
//assign run-function to received data
persons[0].animals[0].run = a.run;
//now this works
persons[0].animals[0].run();
</script>

How about creating a Static method on Person Class, which will accept your server response and create required variables.
This is just an idea. Please see if this fits in your problem.
//Static method
Person.createObjects = function( response ) {
var persons = [];
for ( var p = 0; p < response.length; p++ ) {
//Create Person
var person = new Person( response[p].personName );
//Create Animals
for ( var a = 0; a < response[p].animals.length; a++ ) {
var animal = new Animal( response[p].animals[a].animalName );
//Push this animal into Person
person.animals.push ( animal );
}
//Push this person in persons
persons.push ( person );
}
//Return persons
return persons;
}
//Now Create required persons by passing the server response
var persons = Person.createObjects ( response );

Related

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

JavaScript creating new instance of objects

So I am designing a grade book interface and I have a course defined as:
<script>
course = new Object();
var name;
var gradingareas;
var finalgrade;
</script>
then later I want to create a new instance:
var gradingareas = new Array("Homework", "Classwork", "Exams");
course1 = new course("CS1500", gradingareas, 85);
I have also tried without the var in front to no avail. I get an "Uncaught TypeError: Object is not a function" I am very new to javascript so I don't even know if Im going about this the correct way. Any help is appreciated Thanks.
Your existing code:
// Creates a new, empty object, as a global
course = new Object();
// Creates three new variables in the global scope.
var name;
var gradingareas;
var finalgrade;
There is no connection between the variables and the object.
It looks like you want something more like:
function Course(name, gradingareas, finalgrade) {
this.name = name;
this.gradingareas = gradingareas;
this.finalgrade = finalgrade;
}
Then:
var course1 = new Course("CS1500", gradingareas, 85);
Note the use of a capital letter for naming the constructor function. This is a convention in the JS community.
JS is prototypical, rather than class based and if you are new to it there are advantages to learning this immediately rather than trying to mush classical inheritance models from it, however, classical inheritance is alive and well in JS.
Anyhow, to answer how you would access your variables:
course1.name works fine with the example above.
If you wanted to privatise your data you could take this approach using closure:
var Course = function(name, grade) {
// Private data
var private = {
name: name,
grade: grade
}
// Expose public API
return {
get: function( prop ) {
if ( private.hasOwnProperty( prop ) ) {
return private[ prop ];
}
}
}
};
Then instantiate a new object:
var course = new Course('Programming with JavaScript', 'A');
and start using all that private data:
course.get('name');
Of course, you'd probably want setters to manipulate that data too ;)
The code that you described does the following:
// Declares a memory variable called course and stores and object in it
var course = new Object();
// Declares three variables
var name;
var gradingareas;
var finalgrade;
These declared variables aren't automatically connected to the object. If you want these properties declared on the object you have 2 options:
Declare them as properties of the object
Declare them on the prototype of of the object
Example1: declare them as properties of the object:
// Declares a memory variable called course and stores and object in it
var course = new Object();
// Access or create new properties with . or [] operator
course.name = 'math';
course.gradingareas = 'muliple';
course['finalgrade'] = 'A'
console.log(course);
Example2: Declare them on the prototype:
// Create a constructor function
function Course (name, grade) {
this.name = name;
this.grade = grade;
}
// course is added on the prototype
Course.prototype.gradingareas = 'some gradingareas';
// the name and the grade are added on the object itself
var course = new Course ('willem', 10);
console.log(course);
To create a very simple object with constructor and default values, you can do :
//My object with constructor
var myObjectWithConstrutorFunction = {
//construtor function with default values in constructor
myConstrutor: function(Name = 'bob', Age = 18){
this.Name = name;
this.Age = age;
}
};
// instance
var myInstance = new myObjectWithConstrutorFunction.myConstrutor();
// show on console
console.log('object with constructor function: ', myInstance);
// show properties
console.log(myInstace.Name, myInstance.Age);
PS : It's a good practice create a constructor's name with the same name of the class, if you are creating a external class.

How can I count the instances of an object?

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

Javascript: How to turn a JSON array of object back in to the Object type sharing prototypes?

If you have an array of product objects created from JSON, how would you add a prototype method to the product objects so that they all point to the same method? How would you train JavaScript to recognize all product objects in an array are instances of the same class without recreating them?
If I pull down a JSON array of Products for example, and want each product in the array to have a prototype method, how would I add the single prototype method to each copy of Product?
I first thought to have a Product constructor that takes product JSON data as a parameter and returns a new Product with prototypes, etc. which would replace the data send from the server. I would think this would be impractical because you are recreating the objects. We just want to add functions common to all objects.
Is it possible to $.extend an object's prototype properties to the JSON object so that each JSON object would refer to exactly the same functions (not a copy of)?
For example:
var Products = [];
Products[0] = {};
Products[0].ID = 7;
Products[0].prototype.GetID = function() { return this.ID; };
Products[1].ID = 8;
Products[1].prototype = Products[0].prototype; // ??
I know that looks bad, but what if you JQuery $.extend the methods to each Product object prototype: create an object loaded with prototypes then $.extend that object over the existing Product objects? How would you code that? What are the better possibilities?
For one, you're not modifying the Products[0].prototype, you're modifying Object.prototype, which will put that function on the prototype of all objects, as well as making it enumerable in every for loop that touches an Object.
Also, that isn't the proper way to modify a prototype, and ({}).prototype.something will throw a TypeError as .prototype isn't defined. You want to set it with ({}).__proto__.something.
If you want it to be a certain instance you need to create that instance, otherwise it will be an instance of Object.
You probably want something like:
var Product = function(ID) {
if (!this instanceof Product)
return new Product(ID);
this.ID = ID;
return this;
};
Product.prototype.GetID = function() {
return this.ID;
};
Then, fill the array by calling new Product(7) or whatever the ID is.
First, one problem is that prototype methods are associated when the object is created, so assigning to an object's prototype will not work:
var Products = [];
Products[0] = {};
Products[0].prototype.foo = function () { return 'hello' } // ***
Products[0].foo(); // call to undefined function
(*** Actually, the code fails here, because prototype is undefined.)
So in order to attach objects, you'll need to assign actual functions to the object:
Products[0].foo = function () { return 'hello'; };
You can create a helper function to do so:
var attachFoo = (function () { // Create a new variable scope, so foo and
// bar is not part of the global namespace
function foo() { return this.name; }
function bar() { return 'hello'; }
return function (obj) {
obj.foo = foo;
obj.bar = bar;
return obj; // This line is actually optional,
// as the function /modifies/ the current
// object rather than creating a new one
};
}());
attachFoo(Products[0]);
attachFoo(Products[1]);
// - OR -
Products.forEach(attachFoo);
By doing it this way, your obj.foos and obj.bars will all be referencing the same foo() and bar().
So, if I'm getting this all correctly, this is a more complete example of KOGI's idea:
// Create a person class
function Person( firstName, lastName ) {
var aPerson = {
firstName: firstName,
lastName: lastName
}
// Adds methods to an object to make it of type "person"
aPerson = addPersonMethods( aPerson );
return aPerson;
}
function addPersonMethods( obj ) {
obj.nameFirstLast = personNameFirstLast;
obj.nameLastFirst = personNameLastFirst;
return obj;
}
function personNameFirstLast() {
return this.firstName + ' ' + this.lastName;
}
function personNameLastFirst() {
return this.lastName + ', ' + this.firstName;
}
So, with this structure, you are defining the methods to be added in the addPersonMethods function. This way, the methods of an object are defined in a single place and you can then do something like this:
// Given a variable "json" with the person json data
var personWithNoMethods = JSON.parse( json ); // Use whatever parser you want
var person = addPersonMethods( personWithNoMethods );
You could do this...
function product( )
{
this.getId = product_getId;
// -- create a new product object
}
function product_getId( )
{
return this.id;
}
This way, although you will have several instances of the product class, they all point to the instance of the function.
Could try doing something like this (without jquery)
Basic prototypal object:
function Product(id){
this.id = id;
}
Product.prototype.getId() = function(){return this.id;};
var Products = [];
Products[0] = new Product(7);
Products[1] = new Product(8);
Products[2] = new Product(9);
alert(Products[2].getId());
IMO I found a pretty good answer right here:
Return String from Cross-domain AJAX Request
...I could serialize my
data in the service as a JSON string
and then further wrap that in JSONP
format? I guess when it comes over to
the client it would give the JSON
string to the callback function.
That's not a bad idea. I guess I would
also have the option of sending a
non-JSON string which might allow me
to just use eval in the callback
function to create new Person objects.
I'm thinking this would be a more
efficient solution in both speed and
memory usage client-side.

How to use JSON to create object that Inherits from Object Type?

I know how to use JSON to create objects, but there doesn't seem to be away to use JSON to create an object that is of a specific object type.
Here's an example of an Object and creating an instance of it:
Person = function() { };
Person.prototype = {
FirstName: null,
GetFirstName: function() {
return this.FirstName;
}
};
//Create an instance of the Person Object
var me = new Person();
me.FirstName = "Chris";
alert(me.GetFirstName()); //alert the FirstName property
Now, I would like to use JSON to create a new Person object so that the GetFirstName function works on it.
Here's something like that I'm looking to do (but this code doesn't work):
var you = new Person() { FirstName: "Mike" };
// OR
var you = new Person{ FirstName: "Mike" };
Is there anyway to use JSON to create an object that is of a specific type?
UPDATE: My sample with the Person object is just to simplify the question. In fact, I am unable to modify the constructors of the actual objects that I need to create instances of. The objects are part of a third-party library.
UPDATE: Using some of the suggestions below, I was able to figure out a way to create an object that inherits from the original, and accept JSON in it's constructor. This is neat!
personWrapper = function(obj){
for(var o in obj){
this[o] = obj[o];
}
};
personWrapper.prototype = new Person();
var you = new personWrapper({FirstName: "Chris"});
alert(you.GetFirstName());
alert(you instanceof Person); // returns True - we are successfully inheriting from Person!
I don't imagine so. I'd create a function on the Person class to initialise from a JSON object if I were you.
function Person() {
this.loadFromJSON = function(json) {
this.FirstName = json.FirstName;
};
}
If you didn't know what class the JSON object was representing beforehand, perhaps add an extra variable into your JSON.
{ _className : "Person", FirstName : "Mike" }
And then have a 'builder' function which interprets it.
function buildFromJSON(json) {
var myObj = new json["_className"]();
myObj.loadFromJSON(json);
return myObj;
}
Update: since you say the class is part of a third-party library which you can't change, you could either extend the class with prototyping, or write a function which just populates the class externally.
eg:
Person.prototype.loadFromJSON = function(json) {
// as above...
};
or
function populateObject(obj, json) {
for (var i in json) {
// you might want to put in a check here to test
// that obj actually has an attribute named i
obj[i] = json[i];
}
}
You could allow new Person() to accept an object to populate attributes with as a parameter.
var you = new Person({ firstName: 'Mike' });
You can derive an object from theirs. Your constructor can accept the object you want, but call their constructor in an unaffected fashion:
function yourWrapper(obj) {
theirObject.call(this);
for (var s in obj) {
this[s] = obj[s];
}
}
yourWrapper.prototype = new theirObject();
Or something like that :)

Categories

Resources