2

I have a JavaScript object created from JSON. I really need to set its prototype property to be a different object because of JS5 getters and setters. Here is an example of what I need that works on Chrome:

function MyObj() { }
MyObj.prototype = { 
  get myProp : function () { return this._myProp; },
  set myProp : function (arg) { this._myProp = arg; }
}

... stuff ...

var instance = JSON.parse(result);
instance.constructor = MyObj;
instance.__proto__ = MyObj.prototype;

With this code, I can get and set properties on instance using the getters and setters defined in the prototype. However, this is not portable and will not work on IE, (nor node, I think).

What is the proper and portable way of doing this?

Andrew Eisenberg
  • 28,387
  • 9
  • 92
  • 148
  • define your use of "portable" in this use case please. – Brian Vanderbusch Mar 18 '13 at 20:17
  • possible duplicate of [Setting prototype for Object Literal](http://stackoverflow.com/questions/15472005/setting-prototype-for-object-literal) – Dagg Nabbit Mar 18 '13 at 20:17
  • @GGG Missed that question when I searched earlier. It doesn't exactly answer what I am trying to do. It is suggesting that I use Object.create(...), but since JSON is instantiating the object, I cannot. – Andrew Eisenberg Mar 18 '13 at 20:22
  • @BrianVanderbusch Should work on any ECMA5 compatible runtime. – Andrew Eisenberg Mar 18 '13 at 20:23
  • @AndrewEisenberg if you have `get` and `set`, I'm pretty sure you have `__proto__`, no? – Dagg Nabbit Mar 18 '13 at 20:27
  • so you're trying to extend the JSON object by implementing the interface of your own custom object? – Brian Vanderbusch Mar 18 '13 at 20:28
  • He's trying to assign a prototype to an existing object, a subject that has been discussed here ad nauseum. We have `__proto__` and we narrowly missed having a horribly awkward triangle operator. We have `Object.create()` as a workaround, and its predecessor, the "prototype dance." Those are pretty much the only options, it's not going to change. – Dagg Nabbit Mar 18 '13 at 20:36
  • @GGG unfortunately, no. get and set are ECMA5 compliant. `__proto__` is not. – Andrew Eisenberg Mar 18 '13 at 20:36
  • @BrianVanderbusch Exactly. – Andrew Eisenberg Mar 18 '13 at 20:39
  • @AndrewEisenberg ah, I see, IE 9 and 10 have get and set don't they... I realize `__proto__` is not in ES5, but I was thinking that `get` and `set` only worked in some subset of browsers that are not IE. – Dagg Nabbit Mar 18 '13 at 20:39

2 Answers2

2

Think the other way around:

Create a new instance of MyObj and then copy the properties of instance to it. Or give MyObj a constructor that does that, when you provide it with an argument:

function MyObj(instance) {
 if (instance) {
   //copy properties

This way you may be even able to do

var instance = new MyObj(JSON.parse(result));
a better oliver
  • 26,330
  • 2
  • 58
  • 66
  • I like this idea. I will try it out. However, I also need to be able to support a different kind of constructor, like this: new MyObj(prop1, prop2, prop3); But I should be able to overload the constructor. – Andrew Eisenberg Mar 18 '13 at 20:38
  • this was my thought as well.. beat me to the punch! – Brian Vanderbusch Mar 18 '13 at 20:45
  • @AndrewEisenberg: if prop1 is not an object you can check its type. If it is an object, then you know it's the instance. – a better oliver Mar 18 '13 at 20:48
  • In the end, my solution most closely looks like yours and so I am giving you the correct answer. I decided to go with a separate revive() function defined on MyObj. I don't like the idea of overloading constructors and functions in JS. – Andrew Eisenberg Mar 18 '13 at 23:55
1

You could try using a JSON reviver function.

function revive(json) {

  var m = new MyObj(), y;

  return JSON.parse(json, function(k, v){ 
    if (!y) y = this; 
    return k == "" ? m : this == y ? m[k] = v : v 
  });

}

Call revive('... a json string ...'), it will spit out an instance of MyObj with the properties defined in the JSON string.

Caveat: this will only work if the first item in your JSON object is a primitive value. If that's not possible in your case, here's a rather ugly workaround:

function revive(json) {

  var m = new MyObj(), c = json.charAt(0), y, a;

  if (c == '[') {
    a = json = '[0,' + json.substring(1);
  } else if (c == '{') {
    json = '{"@":0,' + json.substring(1);
  }

  return JSON.parse(json, function(k, v){ 
    if (!y) { y = this; return; } 
    return k == "" ? m : this == y ? m[a ? k - 1 : k] = v : v 
  });

}
Dagg Nabbit
  • 75,346
  • 19
  • 113
  • 141
  • Nice. I had no idea about reviver functions. – Andrew Eisenberg Mar 18 '13 at 21:39
  • Yeah, this sounded good when I started writing it, but really you might as well just do the usual `JSON.parse` and then copy the properties over. This is more of a curiosity than anything, I guess. – Dagg Nabbit Mar 18 '13 at 21:42
  • In the end, I didn't want to use a revive function. Although a neat idea, I wanted to encapsulate the converting to MyObj in the MyObj code, and not where we get the json string. Thanks for the answer anyway. – Andrew Eisenberg Mar 18 '13 at 23:56