1

It was my understanding that every javascript object had a prototype even if it were just the one from Object.prototype

I have an asp.net webservice proxy that gets me participant data from the server. That part works fine. Here's the code:

        atomWebServiceProxy.GetInteractionParticipants(interactionId,
            function (participantsResult) {
                var assetId, pendingPathFetches;
                participants = participantsResult;
                pendingPathFetches = participants.length;
                document.title = participants.length + " participants:";
                for (var i = 0; i < participants.length; i++) {
                    assetId = participants[i].ASSETID;
                    document.title += " " + participants[i].DISPLAYID;
                    atomWebServiceProxy.GetInteractionPath(interactionId, assetId,
                        function (pathResult, participant) {
                            participant.path = pathResult;
                            pathResult.participant = participant;
                            if (--pendingPathFetches == 0) {
                                setTimeout(afterInit, 20);
                            }
                        },
                        function (error, participant) {
                            alert(participant.DISPLAYID + " reported this error: " + error.get_message());
                        },
                        participants[i]
                    );
                }
            },
            function (error) {
                alert(error.toString());
            });

You may notice that there are nested calls and I have added a path property to each participant object, expressing in an object model the fact that it is the path data for that particular participant. This also works fine.

Then I tried to add a method to the prototype like this:

var foo = participants[0];
foo.prototype.redraw = function(map) {
   ... //code that redraws
};

To my great surprise, I got an exception claiming that prototype is null:

JavaScript runtime error: Unable to set property 'redraw' of undefined or null reference

What is going on here?

This question seems to address the problem but I cannot see how to apply this information to this situation, since I do not know how to refer to a constructor that is not defined in my code.

In case this is useful info, inspection of participants.constructor in the immediate window at run-time appears to indicate that the constructor was Array()

Should I write a constructor that copies the fields into an object and sets a prototype? If so is there a javascript equivalent to C# reflection so I don't have to rewrite this for every type of query result object? NOTE coma answered this part of the question in comments while I was updating the question.

Community
  • 1
  • 1
Peter Wone
  • 17,965
  • 12
  • 82
  • 134
  • Have you tried `foo.constructor.prototype.redraw =`? – thebreiflabb May 06 '13 at 08:39
  • Is it JSON? take a look at http://stackoverflow.com/questions/5873624/parse-json-string-into-a-particular-object-prototype-in-javascript – coma May 06 '13 at 08:41
  • Using foo.constructor.prototype.redraw causes jQuery-1.9.1 to barf at line 2816 saying that handlers is undefined, I did actually try that. – Peter Wone May 06 '13 at 08:42
  • 2
    @thebreiflabb: `foo.constructor.prototype` will *usually* be the prototype of the object, but it isn't *necessarily* its prototype. The `constructor` property can be written to (and some "inheritance helper" libraries fail to set it correctly in the first place), and separately the constructor's `prototype` can be replaced after creating an object. Both occurrences are unusual, though. – T.J. Crowder May 06 '13 at 08:49

2 Answers2

4

In this particular case, you're probably better off just adding the function to the object itself, not trying to add it to the prototype.

But to answer your question "How can I set the prototype when I don't know the name of the constructor?":

You can't set the prototype of an existing object (not in an a standard way). The prototype is assigned at construction time and cannot (in a standard way) be changed. You can change the properties of an object's prototype, but not the object that is its prototype.

The prototype of an object is not available from a property on it called prototype. The prototype property relates to functions. It's the object that is assigned as the prototype of objects created via that function using a new expession (e.g., new Foo() assigns Foo.prototype as the prototype of the newly-created object).

You can get the prototype of an object in ES5 via Object.getPrototypeOf. In some pre-ES5 implementations, it's available via the non-standard __proto__ property. (And in some of those implementations, __proto__ can be written to, which is why I said you couldn't swap out the prototype object in a standard way — you can in that non-standard way in implementations that support it.)

So in an ES5 environment, you could extend the prototype of an object like this:

Object.getPrototypeOf(theObject).newstuff = "foo";

But beware that that changes the prototype, and other objects may well be using it! For example:

// A constructor function, and a prototype to assign to objects created with it
function Foo() {
}
Foo.prototype.hi = function() {
    console.log("Hi");
};

// Create two objects
var f1 = new Foo();
var f2 = new Foo();

// Add something to the prototype of `f1`:
Object.getPrototypeOf(f1).there = function() {
    console.log("there");
};

// Since `f1` and `f2` *share* the same prototype object,
// `f2` now has it too!
f2.there(); // "there"

Live Copy | Source

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • This is an outstanding response. You have explained things much better than the three textbooks on my desk, and rather more succinctly. – Peter Wone May 06 '13 at 08:58
  • @PeterWone: Wow, thanks! If so, you may like my (anemic little) blog: http://blog.niftysnippets.org/ and/or my [`Lineage` inheritance helper](http://code.google.com/p/lineagejs/). – T.J. Crowder May 06 '13 at 10:03
  • Write a book. You have the knack for it. I don't think you'd have much drama getting a gig with O'reilly, or you could just self-publish, with the benefit of paying being a printable PDF (personally I prefer XPS because you don't need to install a viewer, but that's a holy war all its own). – Peter Wone May 06 '13 at 12:52
1

To add a method to an object instead of a constructor you simply add it directly instead of via the prototype:

var foo = participants[0];
foo.redraw = function(map) {
   ... //code that redraws
};
slebetman
  • 109,858
  • 19
  • 140
  • 171
  • And within the body of this function I can refer to "this" and expect it to resolve to the object on which it was invoked? – Peter Wone May 06 '13 at 08:57
  • Yes. To be more specific "this" will refer to the object named by the word before the last dot. So for example if you do something like `r = foo.redraw` and then call `r()` the `this` in that call will not refer to `foo` but will refer to the global object (window) instead. – slebetman May 06 '13 at 12:30
  • See this for a longer explanation on how `this` works in js: http://stackoverflow.com/questions/13441307/how-does-the-this-keyword-in-javascript-act-within-an-object-literal/13441628#13441628 – slebetman May 06 '13 at 12:32
  • 1
    This is also a quite helpful answer but if I may borrow from Highlander, there can be only one. – Peter Wone May 06 '13 at 12:38