0

I understand that is question may have been touched on in other Q&A's (such as the How does JavaScript .prototype work? question), but (I hope!) this is a more specific question around WHY some "overrides" using .prototype work in some scenarios, and some do not. To help illustrate (from an example at ejohn.org):

function Ninja() {
    this.swingSword = function() { return true; };
};
Ninja.prototype.swingSword = function() { return false; };

var my_ninja = new Ninja();
alert(my_ninja.swingSword());

The above example will alert "true", which makes sense according to the ejohn.org website's example and explanation, but then subsequently this works too, completely contradicting what I thought was impossible based on the above code:

var _send = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function(){
    alert("Overridden!");
    _send.apply(this, arguments);
};

How can both hold true? One is accurately not being overridden, the other is? They are both Objects as far as I can tell, and the swingSword and send functions are both, well, functions? So how can we override the XMLHttpRequest's send function, but not the Ninja's swingSword function?

Thank you in advance for any insight and assistance!

Community
  • 1
  • 1
Chris Kempen
  • 9,491
  • 5
  • 40
  • 52

1 Answers1

1

There is no real concept of overriding in JavaScript. When you instantiate using new, the prototype object is simply referenced in the new object (as __proto__) and any accesses to properties not present in the object will be routed to the prototype object instead. Still, the "constructor" function is called so you get a chance to modify the new object before it is used by the caller.

In the constructor, the new object is referenced as this, as you know. In your case, you will actually add the swingSword function to the new object where it didn't exist before. If you didn't, all accesses to the swingSword property (function) would fallback to the prototype. In other words, you "override" the prototype function, not the other way around!

Make sure you understand the order of execution. Just because you setup the function on the prototype after defining the constructor doesn't mean it takes precedence. In reality, the constructor is executed when using new.

It should now be clear why the second scenario works; the constructor of XHR doesn't create a send function, so the one on the prototype object will always be used. Since you replace it on the original prototype, you've essentially "overridden" it. By the way, this is why the original is saved in the _send variable; you wouldn't be able to reference it or restore it otherwise!

tne
  • 7,071
  • 2
  • 45
  • 68
  • Ohh I see! So there is no `send` function being created in the XHR constructor! Everything is making more sense now, thank you!! So then a small spin-off question then, how do you know then if available functions are constructor-created or otherwise created? – Chris Kempen Feb 02 '16 at 11:55
  • 1
    @ChrisKempen Good question! And thankfully, it has a simple answer: Use [`hasOwnProperty`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty). – tne Feb 02 '16 at 12:04
  • Ahhh thank you! My headache as dissipated finally! +1 – Chris Kempen Feb 02 '16 at 12:06
  • 1
    @ChrisKempen I should add; this lets you know if the property is on the object itself or the prototype, your question was actually a little different now that I read it again. You can create properties on an object outside the constructor `my_ninja.swingSword = function ...` just like you can can create properties on prototypes inside the constructor. Prototypes are just normal objects, and constructors are just normal functions. Under that perspective, you have no way to know how the property was created. (I guess it wasn't so simple in the end.) – tne Feb 02 '16 at 12:06
  • You actually did just stumble on a good point here, and I hope that someone who read's your solution also dives into these comments as they're invaluable. When you said "...Prototypes are just normal objects...", my mind just exploded and I finally have the deep understanding of them that I was looking for...perhaps inspecting the object in the console would give better insight into where functions are resting, but if you need to get to that level then I feel you you're really stumbled into the land of highly-specific JS requirements. Thank you again :) – Chris Kempen Feb 02 '16 at 12:21
  • @ChrisKempen Glad to hear it. I don't believe you should hold on exploring and experimenting, and I'd even say those mechanisms are core to any codebase (not just to address highly-specific requirements). It's probably one of the main reasons JS often gets a bad rap for being difficult to learn; nothing can be easily explained "declaratively" ("this is how things *are*"), instead they can only be correctly explained "imperatively" ("this is how things arbitrarily *work*"). In fact, the ES spec looks like procedural code, there's barely any high-level abstractions available to reason with. – tne Feb 02 '16 at 12:37