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

前端 未结 9 1589
故里飘歌
故里飘歌 2020-12-23 14:05

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

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

Now,

相关标签:
9条回答
  • 2020-12-23 14:48

    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());
    
    0 讨论(0)
  • 2020-12-23 14:51

    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.

    0 讨论(0)
  • 2020-12-23 14:54

    You don't need to use prototypes in order to bind a custom method in your barebone object.

    Here you have an elegant example that don't pollute your code avoiding redundant code

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

    Example available in https://jsfiddle.net/bmde6L0r/

    0 讨论(0)
  • 2020-12-23 15:00

    In other words you want to change prototype (a.k.a. class) of existing object. Technically you can do it this way:

    var Person = {
      function fullName() { return this.firstName + " " + this.lastName; }
    };
    
    // that is your PROFIT function body: 
    personData.__proto__ = Person ;
    

    After that if you will get true on personData instanceof Person

    0 讨论(0)
  • 2020-12-23 15:01

    Anonymous objects don't have a prototype. Why not just have this:

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

    If you absolutely must use a method call instead of a function call, you can always do something similar, but with an object.

    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 讨论(0)
  • 2020-12-23 15:07

    If you're dealing with plain JSON data then the prototype of each person object would simply be Object.prototype. In order to make it into an object with a prototype of Person.prototype you'd first of all need a Person constructor and prototype (assuming you're doing Javascript OOP in the traditional way):

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

    Then you'd need a way to turn a plain object into a Person object, e.g. if you had a function called mixin which simply copied all properties from one object to another, you could do this:

    //example JSON object
    var jsonPerson = {firstName: "Bjarne", lastName: "Fisk"};
    
    var person = new Person();
    mixin(person, jsonPerson);
    

    This is just one way of solving the problem but should hopefully give you some ideas.


    Update: Now that Object.assign() is available in modern browsers, you could use that instead of writing your own mixin function. There's also a shim to make Object.assign() work on older browsers; see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Polyfill.

    0 讨论(0)
提交回复
热议问题