2

A common operation in JavaScript is to replace an object method with a new one while still calling the original method. This is often done like this:

var originalMethod = obj.method;
obj.method = function(arg) {
    // call original method
    originalMethod.call(this, arg);
    // do my stuff new stuff
}

I'd like to create a utility function for a specific object method that handles the chaining part so that the user doesn't need to be concerned about it. One option is to use the above design, in which each added function is wrapped in a new function that calls the previous one. Another option is to create an array of functions and make the object method just iterate over the array, calling each function in turn.

function addXyzMethod1(fn) {
    if (obj.method) {
        var previousMethod = obj.method;
        obj.method = function(value) {
            value = previousMethod.call(this, value);
            return fn.call(this, value);
        };
    } else {
        obj.method = fn;
    }
}

function addXyzMethod2(fn) {
    if (obj._methodList) {
        obj._methodList.push(fn);
    } else if (obj.method) {
        obj._methodList = [obj.method, fn];
        obj.method = function(value) {
            for (var i = 0, n = obj._methodList.length; i < n; i++) {
                value = obj._methodList[i].call(this, value);
            }
            return value;
        };
    } else {
        obj.method = fn;
    }
}

At this point, I can't decide which method to use since I feel the trade-offs of each are about even. So my question here is: Which of these method is better (more natural, or accepted, or quicker) or is there another method that improves on both of these?

  • 2
    You might want to .apply(this,arguments) instead of .call(this,arg) - passing all the arguments to the function automatically. – Benjamin Gruenbaum Jul 11 '13 at 22:06
  • Did you have a specific use case in mind? Your two approaches along with @BenjaminGruenbaum's all work, but they have different characterstics. – FizzyTea Jul 11 '13 at 23:09
  • Using apply is more tricky because I'm passing the return value of one function to the other. So then I'd need to use .apply(this, [value].concat(Array.prototype.slice.call(arguments, 1)). – Michael Best Jul 12 '13 at 1:04
1

This question is quite similar, or at least comes down to a similar question here: https://stackoverflow.com/questions/660337

Reading the favored answer there, I would recommend you to stick with addXyzMethod1 if you know that you do not have to overwrite the function so often that you are running into the dangers of a stack overflow.

If you want an alternative solution (which would be desirable in my opinion), a better understanding on what you are trying to do exactly would be helpful. I have not required this technique in a long time and from my experience this is bad practice in most cases. There are design patters which help you avoid this situation. E.g. use composition. Have an object representing a collection of functions (can be a simple array of functions) and inject that one into your object via its constructor and store it as a member. Then use it in that method you originally wanted to replace. The collection you injected, you can still add and remove functions from it. This would be kind of similar to your addXyzMethod2 just without having that global util function and without the need to overwrite your object's original method.

  • The link you provided does a good job of showing the difference of opinion on recursion vs looping. I'm quite sure that a stack overflow won't be a problem, and I've decided to go with the recursive solution. – Michael Best Jul 15 '13 at 22:21
1

What you are trying to do is a JavaScript version of call super, which is described as a code smell by Martin Fowler.

I would suggest instead that you either

  1. Contain the "super" object instead of inheriting from it.
  2. Use template method, such as below

    templateMethod = function () {
         baseMethod();
         if (overrideMethod) {
              overrideMethod();
         }
    };
    

Other objects would call templateMethod. The overrideMethod is where child objects would have their code.

  • Thanks for the link to Martin Fowler's article. That was quite informative. And actually my purpose in having these functions is to avoid code smell. – Michael Best Jul 15 '13 at 22:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.