27

In this code I created a function called someFunction. Then I modified Function.prototype.apply and call methods. So instead of my function code is working I am running my interception code (which shows an alert). But neither "call" nor "apply" intercepts direct method call. Is it possiple to intercept this?

Function.prototype.call = function(){alert("call");};
Function.prototype.apply = function(){alert("apply");};
function someFunction(){}
window.onload = function(){
    someFunction.call(this); //call alert is shown
    someFunction.apply(this); //apply alert is shown
    someFunction(); //how can I intercept this?
}
Matt Ball
  • 354,903
  • 100
  • 647
  • 710
yilmazhuseyin
  • 6,442
  • 4
  • 34
  • 38
  • Yes. Now, ALL function calls can be caught, even if the method being called does not exist. See https://stackoverflow.com/a/54172062/286335 – cibercitizen1 Nov 29 '21 at 07:43

6 Answers6

33

You can only override a known function by setting another function in its place (e.g., you can't intercept ALL function calls):

(function () {
    // An anonymous function wrapper helps you keep oldSomeFunction private
    var oldSomeFunction = someFunction;

    someFunction = function () {
        alert("intercepted!");
        oldSomeFunction();
    }
})();

Note that, if someFunction was already aliased/referenced by another script before it was changed by this code, those references would still point to the original function not be overridden by the replacement function.

Andy E
  • 338,112
  • 86
  • 474
  • 445
  • Maybe I can change Function.constructor function so every returned function will have your wrapper around it. – yilmazhuseyin Aug 04 '10 at 14:36
  • 2
    @yilmazhuseyin: no, you can't. Changing the *Function* constructor would only allow you to override functions created with `new Function(str)`. – Andy E Aug 04 '10 at 14:49
  • yes you are right. function newConstructor(){alert("a");}; Function.prototype.constructor = newConstructor; did not work. – yilmazhuseyin Aug 04 '10 at 14:56
  • 1
    It's worth noting that `someFunction` in your example is the function you wish to intercept/extend. I had it backward when I first tried this but eventually figured out I was reading this wrong. Great example besides that. – Dan Aug 14 '14 at 22:42
  • I wanted to inject an function before another one and I needed to know about this and `Function.prototype.call` to pass the correct `this` to my hooked function. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call – Motomotes Mar 25 '15 at 03:20
  • Now, ALL function calls can be caught, even if the method being called does not exist. See https://stackoverflow.com/a/54172062/286335 – cibercitizen1 Nov 29 '21 at 07:42
9
Function.prototype.callWithIntercept = function () {
    alert("intercept");
    return this.apply(null, arguments);
};

var num = parseInt.callWithIntercept("100px", 10);

It is worth noting that in newer versions of JS, there are Proxy objects you can use: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy

Thomas Eding
  • 35,312
  • 13
  • 75
  • 106
3

There is a chance you can intercept direct function call. This requires:

  • Either the function is created by Function.prototype.bind and you have to overwrite Function.prototype.bind before creating the function, or
  • The function is created from Function() (or new Function()) and you also have to overwrite Function function before creating the target function.

If neither of the above two can be met, the only way to intercept a direct call is to wrap the target function, which is the solution provided by AndyE https://stackoverflow.com/a/3406523/1316480

For a function that is created by function literal and is hidden in private scope, there is no way to intercept a direct call to it.

I have a blog post concludes all of these: http://nealxyc.wordpress.com/2013/11/25/intercepting-javascript-function/

Community
  • 1
  • 1
Neal Xiong
  • 1,067
  • 10
  • 8
2

You could iterate over the global scope and replace any objects of function type you find which aren't "yours".

jhurshman
  • 5,861
  • 2
  • 26
  • 16
1

Brilliant, love it :)

const originalApply = window.Function.prototype.apply;
window.Function.prototype.apply = function(){
    console.log("INTERCEPTING APPLY", arguments);
    return originalApply.call(this, ...arguments);
};
Martin Zvarík
  • 2,120
  • 22
  • 27
0

You can achieve this with a Proxy.

First define a handler with an apply trap that intercepts calls to the function. Then, using that handler, set the function to be a proxy onto itself. Example:

function add(a, b){
  return a + b;
}

const handler = {
  apply: function(target, thisArg, argumentsList) {
    console.log('add was called with ' + argumentsList.join(' and '));
    return target(...argumentsList);
  }
};

add = new Proxy(add, handler);

var m = add(3, 5);
console.log('m = ', m);

var n = add(12, 8);
console.log('n = ', n);
Majid Fouladpour
  • 29,356
  • 21
  • 76
  • 127