Overriding a JavaScript function while referencing

2019-01-02 16:47发布

I have a function, a(), that I want to override, but also have the original a() be performed in an order depending on the context. For example, sometimes when I'm generating a page I'll want to override like this:

function a() {
    new_code();
    original_a();
}

and sometimes like this:

function a() {
    original_a();
    other_new_code();
}

How do I get that original_a() from within the over-riding a()? Is it even possible?

Please don't suggest alternatives to over-riding in this way, I know of many. I'm asking about this way specifically.

11条回答
梦该遗忘
2楼-- · 2019-01-02 17:14

In my opinion the top answers are not readable/maintainable, and the other answers do not properly bind context. Here's a readable solution using ES6 syntax to solve both these problems.

const orginial = someObject.foo;
someObject.foo = function() {
  if (condition) orginial.bind(this)(...arguments);
};
查看更多
浪荡孟婆
3楼-- · 2019-01-02 17:19

So my answer ended up being a solution that allows me to use the _this variable pointing to the original object. I create a new instance of a "Square" however I hated the way the "Square" generated it's size. I thought it should follow my specific needs. However in order to do so I needed the square to have an updated "GetSize" function with the internals of that function calling other functions already existing in the square such as this.height, this.GetVolume(). But in order to do so I needed to do this without any crazy hacks. So here is my solution.

Some other Object initializer or helper function.

this.viewer = new Autodesk.Viewing.Private.GuiViewer3D(
  this.viewerContainer)
var viewer = this.viewer;
viewer.updateToolbarButtons =  this.updateToolbarButtons(viewer);

Function in the other object.

updateToolbarButtons = function(viewer) {
  var _viewer = viewer;
  return function(width, height){ 
blah blah black sheep I can refer to this.anything();
}
};
查看更多
明月照影归
4楼-- · 2019-01-02 17:22

The Proxy pattern might help you:

(function() {
    // log all calls to setArray
    var proxied = jQuery.fn.setArray;
    jQuery.fn.setArray = function() {
        console.log( this, arguments );
        return proxied.apply( this, arguments );
    };
})();

The above wraps its code in a function to hide the "proxied"-variable. It saves jQuery's setArray-method in a closure and overwrites it. The proxy then logs all calls to the method and delegates the call to the original. Using apply(this, arguments) guarantees that the caller won't be able to notice the difference between the original and the proxied method.

查看更多
还给你的自由
5楼-- · 2019-01-02 17:26

You could do something like this:

var a = (function() {
    var original_a = a;

    if (condition) {
        return function() {
            new_code();
            original_a();
        }
    } else {
        return function() {
            original_a();
            other_new_code();
        }
    }
})();

Declaring original_a inside an anonymous function keeps it from cluttering the global namespace, but it's available in the inner functions.

Like Nerdmaster mentioned in the comments, be sure to include the () at the end. You want to call the outer function and store the result (one of the two inner functions) in a, not store the outer function itself in a.

查看更多
回忆,回不去的记忆
6楼-- · 2019-01-02 17:26

I've created a small helper for a similar scenario because I often needed to override functions from several libraries. This helper accepts a "namespace" (the function container), the function name, and the overriding function. It will replace the original function in the referred namespace with the new one.

The new function accepts the original function as the first argument, and the original functions arguments as the rest. It will preserve the context everytime. It supports void and non-void functions as well.

function overrideFunction(namespace, baseFuncName, func) {
    var originalFn = namespace[baseFuncName];
    namespace[baseFuncName] = function () {
        return func.apply(this, [originalFn.bind(this)].concat(Array.prototype.slice.call(arguments, 0)));
    };
}

Usage for example with Bootstrap:

overrideFunction($.fn.popover.Constructor.prototype, 'leave', function(baseFn, obj) {
    // ... do stuff before base call
    baseFn(obj);
    // ... do stuff after base call
});

I didn't create any performance tests though. It can possibly add some unwanted overhead which can or cannot be a big deal, depending on scenarios.

查看更多
流年柔荑漫光年
7楼-- · 2019-01-02 17:30

The examples above don't correctly apply this or pass arguments correctly to the function override. Underscore _.wrap() wraps existing functions, applies this and passes arguments correctly. See: http://underscorejs.org/#wrap

查看更多
登录 后发表回答