1

Must be phrasing my question wrong, since I cannot find existing questions on what would seem an often issue.

I have a class Foo.

var Foo = function () {
    return {
        a: function () {}
    };
};

I have another class Bar that is instantiated with an instance of Foo:

var Bar = function (foo) {
    // Does something with foo.  
};

I want to extend the Foo instance to work differently for Bar:

var Bar = function (foo) {
    foo.a = function () {
        return foo.a.apply(this, arguments);
    };

    // Does something with foo.  
};

Doing the way I did it, will cause RangeError: Maximum call stack size exceeded. Is there a way to extend the method itself without a temporary variable?

This is the way to do it using an extra variable:

var Bar = function (foo) {
    var originala;

    originala = foo.a;

    foo.a = function () {
        return originala.apply(this, arguments);
    };

    // Does something with foo.  
};
Gajus
  • 69,002
  • 70
  • 275
  • 438
  • Why don't you want to use a temp variable? And better yet, what are you trying to accomplish with this? It seems a bit questionable. – JLRishe Jan 26 '15 at 13:22

2 Answers2

1

The problem:

You are getting maximum call stack exceeded because you are calling the same function over and over from itself (recursively).

The reason:

When you construct Foo, it gets a function assigned to its property a. All is well.

Next, you construct Bar and attempt to modify behaviour of Foo.a by replacing the function on the object with your own. However, you expect that within the new function's body, Foo.a will still contain the original function. The opposite is true - Foo.a now contains your new, modified function. That is why when you call it, it will keep calling itself until you hit a maximum stack size limit.

This happens because variables' values are resolved at execution time and your new function is executed well after it has been assigned to Foo.a. The original function is lost for this object.

The solution:

Use prototypes. I will not go into detail about using prototypal inheritance as there are plenty floating around.

Note that your approach is a valid one, and frequently the only one. You simply need to keep a reference to the original function somewhere so that you can call it at a later time.

Community
  • 1
  • 1
Robert Rossmann
  • 11,931
  • 4
  • 42
  • 73
  • Extending the prototype would affect all instances of `Foo`. I am monkey patching an existing instance of `foo` to behave differently in a specific domain. It is an accurate explanation of the problem. I was hoping that there is some sort of a name reference that would allow to avoid the temporary variable. – Gajus Jan 26 '15 at 14:21
  • You can put the normal function to the prototype and then monkey-patch per-instance. You could also use a named function, but in order to have access to it you would need to define it outside of the class, which might look ugly in my opinion... – Robert Rossmann Jan 26 '15 at 14:24
0

This becomes really easy when using classes.

class Foo {
    a(){
        return /* Something */
    }
}

class Bar extends Foo {
    a(){
        /* Do something else */
        return super.a()
    }
}

This wasn't possible in most browsers (if any) when this question was asked 6 years ago, but every modern browser now supports classes.

Diriector_Doc
  • 582
  • 1
  • 12
  • 28