1

Say I'm using a library with the code that looks like below:

(function($)
{
    function Library(el, options)
    {
        return new Library.prototype.init(el, options);
    }
    Library.fn = $.Library.prototype = {
        init: function(el, options) {
            this.$elm.on('keydown.library', $.proxy(this.keydown.init, this));
        }
        keydown: function() {
            return {
                init: function(e) {
                    ... somecode
                },
                checkStuff: function(arg1, arg2) {
                    ...someCode
                }
            }
        };
    }
})(jQuery);

It has a plugin system that provides access to this where this is an Object {init: function, keydown: function...}. I want to override the keydown.init function. Normally I could see using something like _.wrap to do it:

somefunc = _.wrap(somefuc, function(oldfunc, args) {
     donewstuff();
     oldfunc.call(this.args);
});

but that doesn't seem to work on the returned nested method e.g.:

this.keydown.init = _.wrap(this.keydown.init, function(oldfunc, args) {
     donewstuff();
     oldfunc.call(this.args);
});

The question might be answered on here but I don't really know the right words to use to describe this style of coding so its hard to search. Bonus points if you let me know if it is even correct to call it a nested returned method?

funkyeah
  • 3,074
  • 5
  • 28
  • 47

3 Answers3

2

This pattern is called a module. The best thing you can do here is cache the method you want to override and call the cached method inside your override:

somefunc._init = somefunc.init;
somefunc.init = function () {
    doStuff();
    this._init();
};

I checked _.wrap and it does the same thing, what your missing as pointed out by another answer is you're losing the context of somefunc. In order to prevent that you can do:

somefunc.init = _.wrap(_.bind(somefunc.init, somefunc), function (oldRef, args) {
    doStuff();
    oldRef.call(this.args);
});
Konstantin Dinev
  • 34,219
  • 14
  • 75
  • 100
  • 1
    isn't this was _.wrap http://underscorejs.org/#wrap does? and/or what is the reason that _.wrap doesn't work in this situation while this will? – funkyeah Apr 20 '15 at 16:27
  • @funkyeah I wasn't aware of what _.wrap does so I checked the documentation and the code on GH and updated my answer. – Konstantin Dinev Apr 21 '15 at 08:51
1

The problem is that your method is run out of context.

You need to set its this context (use .bind() for this)

somefunc.init = _.wrap(somefuc.init.bind(somefunc), function(oldfunc, args) {
     donewstuff();
     oldfunc.call(this.args);
});
Gabriele Petrioli
  • 191,379
  • 34
  • 261
  • 317
  • I thought that underscores _.wrap function uses _.partial http://underscorejs.org/#partial which in turn does something akin to bind... – funkyeah Apr 20 '15 at 16:25
  • It does, but you have lost the context before that. The moment you use `somefuc.init` as a reference you lose the context of `somefuc` – Gabriele Petrioli Apr 20 '15 at 16:35
1

You will need to decorate (read: wrap) the keydown function so that you can wrap the init method of the object it returns:

somefunc.keydown = _.wrap(somefunc.keydown, function(orig) {
    var module = orig(); // it doesn't seem to take arguments or rely on `this` context
    module.init = _.wrap(module.init, function(orig, e) {
         donewstuff();
         return orig.call(this, e);
    });
    return module;
});
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • This seems like the closest answer as the others don't really take into account the keydown object. I played with this a bit trying to get it to fully work but I kept running into one specific issue. In the actual library and not my simplification, they use somefunc.keydown.init. I don't understand how they do this without executing keydown() first but it is definitely happening. That code no longer works when I implement this. I will try to provide even more context above. – funkyeah Apr 21 '15 at 18:03
  • Oh, the [jQuery antipattern](http://stackoverflow.com/a/12143833/1048572)… But it should still work with `$.Library.prototype.keydown`. Maybe that function expression for the `.keydown` value is an IEFE actually? Can you link the library source here? – Bergi Apr 21 '15 at 23:12
  • I can't exactly link because the lib is closed source but I am writing a plugin for Redactor: http://imperavi.com/redactor/docs/how-to-create-plugin/ The example above is intended to be a generic example. Now there are multiple other things in your comment I don't understand so I need to go do some reading! – funkyeah Apr 21 '15 at 23:19
  • Hm, maybe `Redactor` does "install" the plugins dynamically when it needs them, and invokes the function then. At the point where you are overwriting, you should check wether the `typeof ….keydown` is `"function"`, then wrap it like in my answer; if it is an object with an `init` method just wrap that. – Bergi Apr 21 '15 at 23:26