Skip to content

Instantly share code, notes, and snippets.

@cyrus-and
Last active November 23, 2015 15:38
Show Gist options
  • Save cyrus-and/00a0ab85cf893fb6eef8 to your computer and use it in GitHub Desktop.
Save cyrus-and/00a0ab85cf893fb6eef8 to your computer and use it in GitHub Desktop.
Override the behavior of existing functions

Synopsis

override([parent], property, callback);

Override the behavior of existing functions.

  • parent is the object containing the function to be called, it can be:

    • omitted: the window object is used;
    • a constructor function (e.g., String): its prototype is used;
    • an object: used as it is.
  • property is the name of the property of parent to override, if it is a function then property.name is used.

  • callback is used to customize the behavior of the overriden function. The invocation is in the form:

    callback($control, argument_1, ...)
    

    $control is an object with the following properies:

    • original(argument_1, ...) is a reference to the original function;
    • arguments is a proper array of the arguments passed to the original function;
    • replay() is an helper function which is equivalent to calling the original funtion with the arguments currently specified in $control.arguments.

    argument_1, ... are the arguments passed to the original function.

    The return value of callback and its this are the same as the original function.

Examples

// Hijack the default behavior.

override(alert, function ($control, message) {
    console.log('ALERT: "%s"', message);
});

alert(42); // ALERT: "42"

// Log all the requests.

override(XMLHttpRequest, 'open', function ($control, method, url) {
    console.log('AJAX: %s %s', method, url);
    return $control.replay();
});

var ajax = new XMLHttpRequest();
ajax.open('GET', 'http://example.com'); // GET http://example.com
ajax.send();

// Hijack the return value.

override(Object, 'toString', function () {
    return 'lollo';
});

({}).toString(); // lollo

// Change some parameters #1.

override(String, 'replace', function ($control, subStr, newSubStr, flags) {
    newSubStr = '/*' + newSubStr + '*/';
    return $control.original(subStr, newSubStr, flags);
});

'foo bar baz'.replace('bar', 'xxx'); // "foo /*xxx*/ baz"

// Change some parameters #2.

override(Array, function ($control) {
    $control.arguments.unshift('<');
    $control.arguments.push('>');
    return $control.replay();
});

new Array(1,2,3); // ["<", 1, 2, 3, ">"]
function override(parent, property, callback) {
// parent defaults to the window object
if (arguments.length === 2) {
callback = property;
property = parent;
parent = window;
}
// if a "constructor" is passed get its prototype
if (typeof parent === 'function' && parent.hasOwnProperty('prototype')) {
parent = parent.prototype;
}
// here the parent must be an object
if (typeof parent !== 'object') {
throw new Error('parent does not represent a prototype');
}
// attempt to get the property name, if function
if (typeof property === 'function') {
property = property.name;
}
// check hierarchy
if (!(property in parent)) {
throw new Error('parent has no property "' + property + '"');
}
// perform the override
var original = parent[property];
parent[property] = function() {
var control = {
'original': original.bind(this),
'arguments': [],
'replay': function() {
return this.original.apply(null , this.arguments);
}
};
// fill arguments
for (var i = 0; i < arguments.length; i++) {
control.arguments[i] = arguments[i];
}
// the return value is the same of the callback
return callback.apply(this, [control].concat(control.arguments));
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment