3

I'm working on an AngularJS SPA and I'm using prototypes in order to add behavior to objects that are incoming through AJAX as JSON. Let's say I just got a timetable x from an AJAX call.

I've defined Timetable.prototype.SomeMethod = function() and I use https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf in order to set the prototype of x to TimeTable.prototype. I have the polyfill in place too.

If I call x.SomeMethod() this works in IE > 9, FF, Chrome etc. However, IE 9 gives me a headache and says throws an error stating 'x does not have property or member SomeMethod'.

Debugging in IE shows me that the _proto_ of x has SomeMethod() in the list of functions, however, calling x.SomeMethod() gives the same error as described.

How can I make this work in IE9 ?

Jochen van Wylick
  • 5,303
  • 4
  • 42
  • 64
  • MDN is a public wiki that is maintained by interested persons, it doesn't have any authority beyond that, though it is useful. *setPrototypeOf* is an ECMAScript ed 6 method (which is still a draft, not a standard) and therefore not widely supported. The MDN page indicates that it's not supported by IE, however it seems you have discovered that it's supported by IE 10 at least. Also, the [`__proto__` property](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) is not part of any standard (though it is supported by some non–Mozilla browsers). – RobG Mar 19 '14 at 09:10
  • Thanks, but can you point me in the direction of a solution? I've got the polyfill in place in order to get compatibility for older browsers but that doesn't seem to help. Furthermore, Object.create(proto) Is difficult to use since the object is incoming from JSON. Also, what would you recommend as valid JS reference site? – Jochen van Wylick Mar 19 '14 at 10:11
  • The definitive reference is ECMA-262, however MDN is ok for practical examples provided you recognise it's shortcomings. There is no standards compliant way to insert an object on another object's `[[Prototype]]` chain. You could create an instance of the object you want, then copy the own properties of the object to be modified to it. – RobG Mar 19 '14 at 21:04

2 Answers2

3

More comment than answer

The main problem with "extending" a random object retrieved from some other environment is that javascript doesn't really allow random property names, e.g. the random object may have a property name that shadows an inherited property. You might consider the following.

Use the random object purely as data and pass it to methods that access the data and do what you want, e.g.

function getName(obj) {
    return obj.name;
}

So when calling methods you pass the object to a function that acts on the object and you are free to add and modify properties directly on the object.

Another is to create an instance with the methods you want and copy the object's properties to it, but then you still have the issue of not allowing random property names. But that can be mitigated by using names for inherited properties that are unlikely to clash, e.g. prefixed with _ or __ (which is a bit ugly), or use a naming convention like getSomething, setSomething, calcLength and so on.

So if obj represents data for a person, you might do:

// Setup
function Person(obj){
  for (var p in obj) {
    if (obj.hasOwnProperty(p)) {
      this[p] = obj[p];
    } 
  }
}

Person.prototype.getName = function(){
  return this.name;
};

// Object generated from JSON
var dataFred = {name:'fred'};

// Create a new Person based on data
var p = new Person(dataFred);

You might even use the data object to create instances from various consructors, e.g. a data object might represent multiple people, or a person and their address, which might create two related objects.

RobG
  • 142,382
  • 31
  • 172
  • 209
  • Thanks, yes, that solution is quite similar to http://stackoverflow.com/a/5873875/896697. There's also the __proto__ solution in the same post ( http://stackoverflow.com/a/5873934/896697 ) which I have encapsuled with Object.setPrototypeOf. What I could do however, is see if the other implementation works better. There is no property-hiding going on, I'm sure of that, ALSO it works on IE 10 and up and all other browsers... – Jochen van Wylick Mar 20 '14 at 11:40
3

This is how I solved it at the end:

Object.setPrototypeOf = Object.setPrototypeOf || function (obj, proto) {
    if (!isIE9()) {
        obj.__proto__ = proto;
    } else {
        /** IE9 fix - copy object methods from the protype to the new object **/
        for (var prop in proto) {
            obj[prop] = proto[prop];
        }
    }

    return obj;
};

var isIE9 = function() {
    return navigator.appVersion.indexOf("MSIE 9") > 0;
};
Jochen van Wylick
  • 5,303
  • 4
  • 42
  • 64
  • As documentation says: Not supported in the following document modes: Quirks, Internet Explorer 6 standards, Internet Explorer 7 standards, Internet Explorer 8 standards, Internet Explorer 9 standards, Internet Explorer 10 standards. Not supported in Windows 8. https://msdn.microsoft.com/en-us/library/dn342818(v=vs.94).aspx – Jesús López Jan 01 '16 at 19:27