在JavaScript中将普通对象转换为类实例

21
function Person() {
      var self = this;

      self.personName="";
      self.animals=[];
}

function Animal(){
     var self=this;

     self.animalName="";
     self.run=function(meters){
         .....
     }
}

服务器响应:

 [{personName:John,animals:[{animalName:cheetah},{animalName:giraffe}]} , {personName:Smith,animals:[{animalName:cat},{animalName:dog}]} ]

我从服务器获取Person数组。我想将泛型的Person数组转换为具体类型的Person数组,这样我就可以使用。

 persons[0].Animals[2].Run();

我创立了JavaScript的

 Object.create(Person,person1);

但我希望它能跨浏览器版本,并支持数组

  ObjectArray.create(Person,persons);
或者
 Object.create(Person[],persons);

4
你的问题不明确。JavaScript 数组没有类型。 (虽然有一些新的类似数组的东西是有类型的,但基本数组不是。) - Pointy
你所说想要使用的代码意味着从服务器检索到的Person数组将包含对象,例如[ { Animal : [ ... ] }, { Animal : [ ... ] } ] -- 这是你的意思吗? - JMM
我已经添加了示例代码。我认为问题现在很清楚了。 - Oguz Karadenizli
4个回答

24

在JavaScript中创建一个对象需要调用它的构造函数。因此,首先您需要找到正确的参数,这些参数可能不仅仅是属性。之后,您可以将从JSON解析出来的对象的所有公共属性重新分配给已创建的实例。

一般的解决方案是,每个构造函数都接受任何看起来像实例(包括真实实例)的对象并进行克隆。创建正确实例所需的所有内部逻辑将位于正确的位置。

或者更好的方法是在您的类上创建一个静态方法,该方法接受对象并从中创建实例,这比重载构造函数更好:

Person.fromJSON = function(obj) {
    // custom code, as appropriate for Person instances
    // might invoke `new Person`
    return …;
};

你的情况非常简单,因为你没有任何参数,只有公共属性。要将 {personName:John,animals:[]} 更改为对象实例,请使用以下代码:

var personLiteral = ... // JSON.parse("...");
var personInstance = new Person();
for (var prop in personLiteral)
    personInstance[prop] = personLiteral[prop];

你也可以使用Object.assign功能(或例如ES6之前的jQuery.extend)来实现此目的:

var personInstance = Object.assign(new Person(), personLiteral);

创建Animal实例的过程是类似的。

由于JSON不传输有关类的任何信息,因此您必须事先了解结构。在您的情况下,它将是:

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

顺便提一下,你的run方法似乎应该添加到Animal.prototype对象上,而不是每个实例上。


4

看起来你有一些包含原型方法的类,并且你只想让你的对象使用这些方法。

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

如果每个人都支持 __proto__ 属性,这可能会更容易(并且我们可以避免所有的复制)。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();  
});​

这不是Object.create的适当用法,也无法以这种方式工作。 - Bergi
surrogateConstructor 有什么用处,难道不能使用普通的构造函数吗?或者如果你想避免调用它,可以使用 Object.create(constructor.prototype) - Bergi
@Bergi 我确实想避免调用构造函数。然而,这是真的,这样更容易理解。自从 Object.create 不存在之前,我一直习惯于这种设置方式。这就是 https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/create 建议您在浏览器不支持 Object.create 的情况下实现它的方式。 - Ruan Mendes
为什么你在第二个解决方案中没有写“person.prototype = Person.prototype”? - Oguz Karadenizli
@ozz person.prototype 没有指向任何东西。personPerson 的一个实例。构造函数 Person 是包含 .prototype 属性的对象。 - Ruan Mendes

1
首先:在JavaScript中,你没有像C++、Java或C#那样的类。因此,你实际上不能拥有一个类型化数组。
你正在做的事情基本上适用于变量,但不适用于函数。所以你需要先添加函数。请看下面的代码,以获取一个想法。
<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>

1

在Person类上创建一个静态方法,该方法将接受您的服务器响应并创建所需的变量。

这只是一个想法。请看看它是否适合您的问题。

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

修复你的运行变量。你想使用 p 还是 i?此外,我认为这不应该是 Person 类上的静态方法 - 当 JSON 格式更改时,为什么要更改类呢? - Bergi
谢谢Bergi。我把我的变量更新为p了。关于静态方法,是的,这是Person类上的静态方法。而且JSON格式肯定不会改变。如果它发生了变化,那么它就不会自动转换为Person对象。因此,为了兼容性,JSON格式应始终保持相同的格式。 - Sasidhar Vanga

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接