Mike Doe
Mike Doe

Reputation: 17566

Method overload in JavaScript

Consider sample html code:

<head>
    ....
    <script src="/js/MyClass.js"></script>
    <script src="/js/extend.js"></script>
    <script>
        $(document).ready(function($) {
            var $obj=new MyClass();
            $obj.run();
        });
    </script>
</head>
<body>

MyClass.js file:

var MyClass=function()
{
    this.run=function() {
        alert("MyClass");
    }
}

extend.js file:

MyClass.prototype.run=function() {
    alert("Extend");
}

Why does this code alert "MyClass" instead of "Extend"? How to correctly rewrite (overload) class method?

Upvotes: 2

Views: 3080

Answers (3)

Elias Van Ootegem
Elias Van Ootegem

Reputation: 76405

It's all got to do with how JS resolves expressions like <object>.<property/function>. I've previously explained this in detail, but here's the schematic run-down applied to your case:

[    MyClass.run    ]<=========================================================\ \
MyClass[run] ===> JS checks instance for property run                           | |
 /\ ||                                                                          | |
 || || --> property found @instance, resolve value------------------------------| |
 || ||                                                                          | |
 || ===========> MyClass.prototype.run could not be found? check prototype      | |
 ||      ||                                                                     | |
 ||      ||--> OR property found @MyClass.prototype, return---------------------| |
 ||      ||                                                                     | |
 ||      ==========> Object.prototype.run: not found check prototype?           | |
 ||          ||                                                                 | |
 ||          ||--> property found @Object.prototype, return---------------------|-|
 ||          ||                                                                 |=|
 ||          =======> chech prototype of Object.prototype -> undefined~~~~~~~~~~|X|
 ||                                                                             \ /
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~< TypeError can't read property run of undefined

This is All of the places JS can check for the run property. Because your constructor function defines a run property on the instance (this.run = function(){};), the lookup never goes past the first step: "JS checks instance for property run".
The prototype-chain never comes into play.

You ask if, and how, it might be possible to overload JS methods. The short answer is: It isn't, not really. Overloading is a valid OOP technique, that is very handy in traditional, class-based OO models. JS doesn't play that way, it uses a prototypal model instead. Trying to force the prototypal system to work like a traditional OO language is possible (owing to the flexability of the prototypal system), but it requires too much effort to keep up, and often just isn't worth it.
You could compare it to using a regular saloon/sedan car to plow a field. At first, you may be able to, but it won't take long before you get stuck, and have to call on a tractor to tow you out of the field.

If you still want to give it a try, here's how:

function MyClass()
{
    this.run = function()
    {
        console.log('child method');
        var backup = this.run;//store reference to instance property
        delete this.run;//delete instance property
        this.run();//call again
        //but now, the instance property is missing, JS will use the prototype
        this.run = backup;//restore instance property
    };
}
MyClass.prototype.run = function()
{
    console.log('prototype run method');
};
var foo = new MyClass;
foo.run();
//logs:
//child method
//prototype run method

You might find it useful to take a look here, it's the previous answer in which I explained JS's way of resolving expressions a bit more detailed. At the bottom of my answer there, I've also added some more links on the matter, might be worth while looking at them, too...

Upvotes: 5

curiousdannii
curiousdannii

Reputation: 2010

Your MyClass constructor is overriding the prototype. The following code will alert "MyClass", "Extended" and "Overriden" each in turn. The last alert shows that changing the instance's function does not change the prototype's function.

var MyClass=function()
{
    this.run();
    MyClass.prototype.run = function() { alert( "Extended" ) };
    this.run();
    this.run=function() {
        alert("Overriden");
    };
    this.run();
};
MyClass.prototype.run = function() { alert( "MyClass" ); };
var myinstance = new MyClass();
alert( myinstance.run === MyClass.prototype.run );

Upvotes: 0

Thilo
Thilo

Reputation: 262494

The prototype is only used when the object itself does not have the property already.

In your case, you put run on the object itself, so that is what run will resolve to.

In a way, your MyClass is overriding its own prototype.

Maybe you want

var MyClass=function(){};
MyClass.prototype.run = function() {
    alert("MyClass");
}

var overridden = new MyClass();
overridden.run = function() {
     alert("Extend");
}

That would be an instance with a different method than its prototype.

Or you could chain prototypes to simulate a class hierarchy.

Upvotes: 1

Related Questions