web-dev-qa-db-fra.com

Comment ajouter des méthodes au prototype d'un objet (JSON)?

Disons que je reçois un objet JSON de mon serveur, par exemple. Quelques données pour un objet Personne:

{firstName: "Bjarne", lastName: "Fisk"}

Maintenant, je veux quelques méthodes en plus de ces données, par exemple. pour calculer le nom complet:

fullName: function() { return this.firstName + " " + this.lastName; }

Afin que je puisse

var personData = {firstName: "Bjarne", lastName: "Fisk"};
var person = PROFIT(personData);
person.fullName(); // => "Bjarne Fisk"

Ce que je voudrais fondamentalement faire ici est d’ajouter une méthode au prototype de l’objet. La méthode fullName() est générale et ne doit donc pas être ajoutée à l'objet de données lui-même. Comme..:

personData.fullName = function() { return this.firstName + " " + this.lastName; }

... causerait beaucoup de redondance; et sans doute "polluer" l'objet de données.

Quelle est la meilleure pratique actuelle pour ajouter de telles méthodes à un simple objet de données?

MODIFIER:

Un peu en dehors du sujet, mais si le problème ci-dessus peut être résolu, il serait possible de faire du Nice pseudo -pattern matching comme ceci:

if ( p = Person(data) ) {
   console.log(p.fullName());
} else if ( d = Dog(data) ) {
   console.log("I'm a dog lol. Hear me bark: "+d.bark());
} else {
   throw new Exception("Shitty object");
}

Person et Dog ajouteront les méthodes si l'objet data a les bons attributs. Si ce n'est pas le cas, retourne falsy (c.-à-d. Que les données ne pas correspondent/sont conformes).

QUESTION SUR LE BONUS: Est-ce que quelqu'un connaît une bibliothèque qui l'utilise ou le permet (c.-à-d. Le rend facile)? Est-ce déjà un motif javascript? Si oui, comment s'appelle-t-il? et avez-vous un lien qui développe? Merci :)

28
kornfridge

En supposant que votre objet provienne d'une bibliothèque JSON qui analyse la sortie du serveur pour générer un objet, il n'aura en général rien de particulier dans son prototype; et deux objets générés pour des réponses de serveur différentes ne partageront pas une chaîne de prototypes (hormis Object.prototype, bien sûr;)) 

Si vous contrôlez tous les emplacements où une "personne" est créée à partir de JSON, vous pouvez procéder de la manière inverse: créez un objet Personne "vide" (avec une méthode telle que fullName dans son prototype) et étendez-le avec l'objet généré. depuis le JSON (en utilisant $ .extend, _.extend ou quelque chose de similaire). 

var p = { first : "John", last : "Doe"};

function Person(data) {
   _.extend(this, data);
}

Person.prototype.fullName = function() {
   return this.first + " " + this.last;   
}

console.debug(new Person(p).fullName());
20
phtrivier

Il y a une autre possibilité ici. JSON.parse accepte un deuxième paramètre, qui est une fonction utilisée pour faire revivre les objets rencontrés, des nœuds feuille au nœud racine. Donc, si vous pouvez reconnaître vos types en fonction de leurs propriétés intrinsèques, vous pouvez les construire dans une fonction reviver. Voici un exemple très simple de le faire:

var MultiReviver = function(types) {
    // todo: error checking: types must be an array, and each element
    //       must have appropriate `test` and `deserialize` functions
    return function(key, value) {
        var type;
        for (var i = 0; i < types.length; i++) {
            type = types[i];
            if (type.test(value)) {
                return type.deserialize(value);
            }
        }
        return value;
    };
};

var Person = function(first, last) {
    this.firstName = first;
    this.lastName = last;
};
Person.prototype.fullName = function() {
    return this.firstName + " " + this.lastName;
};
Person.prototype.toString = function() {return "Person: " + this.fullName();};
Person.test = function(value) {
    return typeof value.firstName == "string" && 
           typeof value.lastName == "string";
};
Person.deserialize = function(obj) {
    return new Person(obj.firstName, obj.lastName);
};

var Dog = function(breed, name) {
    this.breed = breed;
    this.name = name;
}
Dog.prototype.species = "canine";
Dog.prototype.toString = function() {
    return this.breed + " named " + this.name;
};
Dog.test = function(value) {return value.species === "canine";};
Dog.deserialize = function(obj) {return new Dog(obj.breed, obj.name);};


var reviver = new MultiReviver([Person, Dog]);

var text = '[{"firstName": "John", "lastName": "Doe"},' +
            '{"firstName": "Jane", "lastName": "Doe"},' +
            '{"firstName": "Junior", "lastName": "Doe"},' +
            '{"species": "canine", "breed": "Poodle", "name": "Puzzle"},' +
            '{"species": "canine", "breed": "Wolfhound", "name": "BJ"}]';

var family = JSON.parse(text, reviver)
family.join("\n");

// Person: John Doe
// Person: Jane Doe
// Person: Junior Doe
// Poodle named Puzzle
// Wolfhound named BJ

Cela dépend de votre capacité à reconnaître sans ambiguïté vos types. Par exemple, s'il existait un autre type, même un sous-type de personne, qui possédait également les propriétés firstName et lastName, cela ne fonctionnerait pas. Mais cela pourrait couvrir certains besoins.

9
Scott Sauyet

Si vous utilisez des données JSON simples, le prototype de chaque objet Personne serait simplement Object.prototype. Pour en faire un objet avec un prototype de Person.prototype, vous avez tout d'abord besoin d'un constructeur et d'un prototype Person (en supposant que vous exécutiez Javascript OOP de manière traditionnelle):

function Person() {
    this.firstName = null;
    this.lastName = null;
}
Person.prototype.fullName = function() { return this.firstName + " " + this.lastName; }

Ensuite, vous aurez besoin d’un moyen de transformer un objet simple en un objet Personne, par exemple. Si vous aviez une fonction appelée mixin qui copiait simplement toutes les propriétés d'un objet à un autre, vous pourriez faire ceci:

//example JSON object
var jsonPerson = {firstName: "Bjarne", lastName: "Fisk"};

var person = new Person();
mixin(person, jsonPerson);

Ceci n’est qu’un moyen de résoudre le problème, mais nous espérons pouvoir vous donner quelques idées.


Mise à jour: Maintenant que Object.assign() est disponible dans les navigateurs modernes, vous pouvez l'utiliser au lieu d'écrire votre propre fonction de mixin. Il y a aussi un shim pour faire fonctionner Object.assign() sur des navigateurs plus anciens; voir https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Polyfill .

5
Matt Browne

Vous ne devriez probablement pas faire cela.

JSON vous permet de sérialiser un état, pas un type. Donc, dans votre cas d'utilisation, vous devriez faire quelque chose comme ceci:

var Person = function ( data ) {
    if ( data ) {
        this.firstName = data.firstName;
        this.lastName = data.lastName;
    }
};

Person.prototype.fullName = function ( ) {
    return this.firstName + ' ' + this.lastName;
};

//

var input = '{"firstName":"john", "lastName":"Doe"}';
var myData = JSON.parse( input );
var person = new Person( myData );
4
Maël Nison

En d'autres termes, vous souhaitez modifier le prototype (classe a.k.a.) d'un objet existant . Techniquement, vous pouvez le faire comme suit:

var Person = {
  function fullName() { return this.firstName + " " + this.lastName; }
};

// that is your PROFIT function body: 
personData.__proto__ = Person ;

Après cela, si vous aurez true sur personData instanceof Person

3
c-smile

Utilisez new-ish Object.setPrototypeOf () . (Il est maintenant supporté par IE11 et tous les autres navigateurs.)

Vous pouvez créer une classe/un prototype qui inclut les méthodes souhaitées, telles que votre nom complet (), puis

Object.setPrototypeOf( personData, Person.prototype );

Comme l'indique l'avertissement (sur la page MDN liée ci-dessus), cette fonction ne doit pas être utilisée à la légère, mais cela a du sens lorsque vous modifiez le prototype d'un objet existant et c'est ce que vous semblez être après.

1
Tom

Il n'est pas nécessaire d'utiliser des prototypes pour lier une méthode personnalisée à votre objet barebone.

Ici vous avez un exemple élégant qui ne pollue pas votre code en évitant le code redondant 

var myobj = {
  title: 'example',
  assets: 
  {
    resources: ['zero', 'one', 'two']
  }
}

var myfunc = function(index)
{
    console.log(this.resources[index]); 
}

myobj.assets.giveme = myfunc

myobj.assets.giveme(1);

Exemple disponible dans https://jsfiddle.net/bmde6L0r/

0
Juan Lago

Je ne pense pas qu'il soit courant de transporter des méthodes avec des données, mais cela semble être une bonne idée.

Ce projet vous permet d’encoder les fonctions avec vos données, mais il n’est pas considéré comme standard et nécessite bien entendu un décodage avec la même bibliothèque.

https://github.com/josipk/json-plus
0
Billy Moon

Les objets anonymes n'ont pas de prototype. Pourquoi ne pas simplement avoir ceci:

function fullName(obj) {
    return obj.firstName + ' ' + obj.lastName;
}

fullName(person);

Si vous devez absolument utiliser un appel de méthode au lieu d'un appel de fonction, vous pouvez toujours faire quelque chose de similaire, mais avec un objet.

var Person = function (person) { this.person = person; }
Person.prototype.fullName = function () {
    return this.person.firstName + ' ' + this.person.lastName;
}
var person = new Person(personData);
person.fullName();
0
Explosion Pills