21
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);
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
Oguz Karadenizli
  • 3,449
  • 6
  • 38
  • 73
  • 4
    Your question is unclear. JavaScript arrays are not typed. (Well there are new array-like things that are typed but basic arrays are not.) – Pointy Aug 04 '12 at 16:07
  • The code you say you want to use implies that the Person array you're retrieving from the server will contain objects, e.g. `[ { Animal : [ ... ] }, { Animal : [ ... ] } ]` -- is that what you mean? – JMM Aug 04 '12 at 16:14
  • I've added sample code. I think question is clear now. – Oguz Karadenizli Aug 04 '12 at 16:19

4 Answers4

24

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.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
4

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();  
});​
Ruan Mendes
  • 90,375
  • 31
  • 153
  • 217
  • This is not a suitable use for `Object.create`, and won't work this way, too. – Bergi Aug 04 '12 at 17:56
  • What is that `surrogateConstructor` good for, can't you just use the normal one? Or use `Object.create(constructor.prototype)` if you want to avoid calling it. – Bergi Aug 04 '12 at 19:24
  • @Bergi I do want to avoid calling the constructor. However, it's true that it's easier to understand it like that. I have just been used to that way of setting it up since before `Object.create` existed. That is how https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/create suggests you implement `Object.create` if it doesn't exist for your browser. – Ruan Mendes Aug 04 '12 at 19:52
  • Why you did not write "person.prototype= Person.prototype" in your second solution ? – Oguz Karadenizli Aug 04 '12 at 20:59
  • @ozz `person.prototype` does not point to anything. `person` is an instance of `Person`. The constructor function `Person` is the object that contains a `.prototype` property. – Ruan Mendes Aug 05 '12 at 21:34
1

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>
Preli
  • 2,953
  • 10
  • 37
  • 50
1

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 );
Sasidhar Vanga
  • 3,384
  • 2
  • 26
  • 47
  • fix your running variables. Do you want to use `p` or `i`? Also, I don't think this should be a statice method on the `Person` class - why change the class when the JSON format changes? – Bergi Aug 04 '12 at 18:49
  • Thanks Bergi. I updated my variable to p. And regarding the static method. Yes, this is a static method on Person class. And definitely the JSON format will not change. If it changes, then it will not automatically convert to Person object at any case. So, for compatibility, the JSON format should always have the same format. – Sasidhar Vanga Aug 04 '12 at 19:20