Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to add methods to a (JSON) object's prototype?

Let's say I receive some JSON object from my server, e.g. some data for a Person object:

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

Now, I want some methods on top of those data, e.g. for calculating the fullName:

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

So that I can

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

What I basically would want to do here, is to add a method to the object's prototype. The fullName() method is general, so should not be added to the data object itself. Like..:

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

... would cause a lot of redundancy; and arguably "pollute" the data object.

What is the current best-practice way of adding such methods to a simple data object?

EDIT:

Slightly off topic, but if the problem above can be solved, it would be possible to do some nice pseudo-pattern matching like this:

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 and Dog will add the methods if the data object has the right attributes. If not, return falsy (ie. data does not match/conform).

BONUS QUESTION: Does anyone know of a library that either uses or enables this (ie makes it easy)? Is it already a javascript pattern? If so, what is it called; and do you have a link that elaborates? Thanks :)

like image 875
kornfridge Avatar asked Feb 24 '13 17:02

kornfridge


People also ask

Can JSON objects have methods?

JSON is a data format that has its own independent standard and libraries for most programming languages. JSON supports plain objects, arrays, strings, numbers, booleans, and null . JavaScript provides methods JSON.

How can we make methods available on all objects?

How can we make methods available on all objects? Explanation: It is possible to add methods to Object. prototype, making them available on all objects. This is not recommended, however, because prior to ECMAScript5, there is no way to make these add-on methods non enumerable, and if you add properties to Object.


2 Answers

Assuming your Object comes from some JSON library that parses the server output to generate an Object, it will not in general have anything particular in its prototype ; and two objects generated for different server responses will not share a prototype chain (besides Object.prototype, of course ;) )

If you control all the places where a "Person" is created from JSON, you could do things the other way round : create an "empty" Person object (with a method like fullName in its prototype), and extend it with the object generated from the JSON (using $.extend, _.extend, or something similar).

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()); 
like image 193
phtrivier Avatar answered Oct 02 '22 21:10

phtrivier


There is another possibility here. JSON.parse accepts a second parameter, which is a function used to revive the objects encountered, from the leaf nodes out to the root node. So if you can recognize your types based on their intrinsic properties, you can construct them in a reviver function. Here's a very simple example of doing so:

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 

This depends on you being able to unambiguously recognizing your types. For instance, if there were some other type, even a subtype of Person, which also had firstName and lastName properties, this would not work. But it might cover some needs.

like image 36
Scott Sauyet Avatar answered Oct 02 '22 21:10

Scott Sauyet