我能拦截直接调用的函数吗?

27
在这段代码中,我创建了一个名为someFunction的函数。然后,我修改了Function.prototype.apply和call方法。因此,我的函数代码不再起作用,我正在运行我的拦截代码(显示警报)。但是,无论是“call”还是“apply”,都不能拦截直接的方法调用。是否有可能拦截它们呢?
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?
}

是的。现在,所有函数调用都可以被捕获,即使被调用的方法不存在。请参见 https://dev59.com/77Hma4cB1Zd3GeqPGBeB#54172062。 - cibercitizen1
6个回答

33

只有通过设置另一个函数来覆盖已知函数(例如,您无法拦截所有函数调用):

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

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

注意,如果在此代码更改someFunction之前其他脚本已经将其别名/引用,则这些引用仍将指向原始函数而不是被替换的函数。


也许我可以更改Function.constructor函数,这样每个返回的函数都会有你的包装器。 - yilmazhuseyin
2
@yilmazhuseyin:不行,你不能这样做。改变 Function 构造函数只能允许你重写使用 new Function(str) 创建的函数。 - Andy E
是的,你是对的。 function newConstructor(){alert("a");}; Function.prototype.constructor = newConstructor; 没有起作用。 - yilmazhuseyin
1
值得注意的是,你示例中的 someFunction 是你想要拦截/扩展的函数。当我第一次尝试时,我弄反了,但最终发现我读错了。除此之外,这是一个很好的例子。 - Dan
我想在另一个函数之前注入一个函数,我需要了解这个并使用Function.prototype.call将正确的this传递给我的钩子函数。https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/call - Motomotes
现在,所有函数调用都可以被捕获,即使被调用的方法不存在。请参见 https://dev59.com/77Hma4cB1Zd3GeqPGBeB#54172062。 - cibercitizen1

9

我喜欢这个拦截方法。 - TURTLE

3

有一种可能性是你可以拦截直接函数调用。这需要:

  • 要么该函数由Function.prototype.bind创建,并且在创建函数之前必须覆盖Function.prototype.bind,或者
  • 该函数是由Function()(或new Function())创建的,您还必须在创建目标函数之前覆盖Function函数。

如果以上两种情况都不符合,唯一的拦截直接调用的方法是包装目标函数,这是AndyE提供的解决方案https://dev59.com/5XA75IYBdhLWcg3wOWVd#3406523

对于由函数文字创建并隐藏在私有范围中的函数,无法拦截直接调用。

我撰写了一篇博客文章总结了所有这些内容:http://nealxyc.wordpress.com/2013/11/25/intercepting-javascript-function/


2
你可以遍历全局作用域并替换任何不是“你的”函数类型的对象。

1

太棒了,喜欢它 :)

const originalApply = window.Function.prototype.apply;
window.Function.prototype.apply = function(){
    console.log("INTERCEPTING APPLY", arguments);
    return originalApply.call(this, ...arguments);
};

0

你可以通过使用代理来实现这一点。

首先定义一个带有apply陷阱的处理程序,拦截对函数的调用。然后,使用该处理程序,将函数设置为其自身的代理。例如:

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);


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接