扩展本机函数以在函数调用时运行代码 JavaScript中是否有原型或扩展本机函数对象以在函数调用上运行另一个函数

扩展本机函数以在函数调用时运行代码 JavaScript中是否有原型或扩展本机函数对象以在函数调用上运行另一个函数,javascript,function,profiling,interceptor,Javascript,Function,Profiling,Interceptor,当然,这段代码不起作用,但这里的目标是对函数调用执行时间进行基准测试 我知道我可以在函数本身之外,通过不扩展本机函数对象(这通常是个坏主意)来实现这一点,但这实际上只是一个实验,而不是一个严肃的解决方案 我想对多个函数进行基准测试,这样这将是一个不那么繁琐的解决方案 这也是为什么我没有原型化调用和应用属性的原因(这意味着我必须将每个函数调用test()重写为test.call()) 提前谢谢你 不可能有效地拦截JavaScript中的所有方法调用。您始终可以重写Function.prototyp

当然,这段代码不起作用,但这里的目标是对函数调用执行时间进行基准测试

我知道我可以在函数本身之外,通过不扩展本机
函数
对象(这通常是个坏主意)来实现这一点,但这实际上只是一个实验,而不是一个严肃的解决方案

我想对多个函数进行基准测试,这样这将是一个不那么繁琐的解决方案

这也是为什么我没有原型化
调用
应用
属性的原因(这意味着我必须将每个函数调用
test()
重写为
test.call()


提前谢谢你

不可能有效地拦截JavaScript中的所有方法调用。您始终可以重写
Function.prototype.call
Function.prototype.apply
,但在正常调用函数时不会调用这些函数,例如
someFunction()

但是,如果您针对的是来自特定对象的方法,则始终可以深入遍历这些对象,并将每个函数包装到拦截器函数中。但是,请注意,对于进程运行后添加的每个方法,您都必须重新运行该拦截进程

我为您创建了一个示例:

/**
 * AOP utility function developed for learning purposes.
 *
 * @param {Object} o The object to traverse for overriding functions.
 * @param {RegExp} rx A regular expression for matching members.
 * @param {String} pointcut 'before' or 'after'.
 * @param {Function} advice The function to run when the pointcut is met. This function will be passed an {Options} object as last argument.
 * @param {maxDepth} maxDepth The maximum depth for deep-traversal. Defaults to 0.
 *
 * Options object
 *     overrideReturn {Boolean} - True to override the return value of the original function with the return value of the advice. Defaults to false. Pointcuts: before, after
 *     cancel {Boolean} - True to avoid calling the original function. Default to false. Pointcuts: before
 *     overrideArgs {Boolean} - True to override the arguments that will be passed to the original function with the result of the advice. Defaults to false. Pointcuts: before
 *     result {*} - The return value of the original function. Pointcuts: after
 */

function inject(o, rx, pointcut, advice, maxDepth) {
    var pointcuts = {
        before: function (fn1, fn2) {
            return function () {

                var options = injectNewOptions(arguments, BeforeOptions),
                    fn2Result = fn2.apply(this, arguments),
                    fn1Result;

                if (options.cancel) {
                    return fn2Result;
                }

                fn1Result = fn1.apply(this, (options.overrideArgs ? fn2Result : arguments));

                return options.overrideReturn ? fn2Result : fn1Result;
            };
        },
        after: function (fn1, fn2) {
            return function () {
                var fn1Result = fn1.apply(this, arguments),
                    options = injectNewOptions(arguments, Options),
                    fn2Result;

                options.result = fn1Result;

                fn2Result = fn2.apply(this, arguments);

                return options.overrideReturn ? fn2Result : fn1Result;
            };
        }
    },
        Options = {
            overrideReturn: false
        },
        BeforeOptions = Object.create(Options, {
            cancel: {
                enumerable: true,
                writable: true,
                value: false
            },
            overrideArgs: {
                enumerable: true,
                writable: true,
                value: false
            }
        });

    function injectNewOptions(args, baseOptions) {
        var options = Object.create(baseOptions);

        Array.prototype.push.call(args, options);

        return options;
    }

    inject = function (o, rx, pointcut, advice, maxDepth, depth) {
        var k, f;

        maxDepth = maxDepth || 0;
        depth = 0 || depth;

        for (k in o) {
            if (typeof (f = o[k]) === 'function' && rx.test(k)) {
                o[k] = pointcuts[pointcut](f, advice, pointcut);
            } else if (typeof f === 'object' && maxDepth <= depth) {
                inject(f, rx, pointcut, advice, maxDepth, ++depth);
            }
        }
    };

    inject.apply(this, arguments);
}
编辑:

这里值得一提的是@Bergi在评论中所说的话

如果您想评测所有函数,而不是仅评测几个选定函数 首先,你最好使用浏览器的开发工具 一些剧本


为什么这段代码不能像预期的那样工作?并不是这段代码不能工作,只是我希望在任何函数以通常的方式运行时执行这段代码:
test()
,而不是像这样调用我自己的自定义版本:
test.called()
。我可以将其集成到预先存在的代码库中,以对函数调用时间进行基准测试。如果您想评测所有函数,而不是只评测几个选定的函数,最好使用浏览器的开发工具,而不是一些脚本。@Bergi,我在回答中引用了您的评论,因为这非常相关。希望你不介意。非常感谢你的回答!=)这应该是美丽代码的一章@我很高兴能帮上忙!
/**
 * AOP utility function developed for learning purposes.
 *
 * @param {Object} o The object to traverse for overriding functions.
 * @param {RegExp} rx A regular expression for matching members.
 * @param {String} pointcut 'before' or 'after'.
 * @param {Function} advice The function to run when the pointcut is met. This function will be passed an {Options} object as last argument.
 * @param {maxDepth} maxDepth The maximum depth for deep-traversal. Defaults to 0.
 *
 * Options object
 *     overrideReturn {Boolean} - True to override the return value of the original function with the return value of the advice. Defaults to false. Pointcuts: before, after
 *     cancel {Boolean} - True to avoid calling the original function. Default to false. Pointcuts: before
 *     overrideArgs {Boolean} - True to override the arguments that will be passed to the original function with the result of the advice. Defaults to false. Pointcuts: before
 *     result {*} - The return value of the original function. Pointcuts: after
 */

function inject(o, rx, pointcut, advice, maxDepth) {
    var pointcuts = {
        before: function (fn1, fn2) {
            return function () {

                var options = injectNewOptions(arguments, BeforeOptions),
                    fn2Result = fn2.apply(this, arguments),
                    fn1Result;

                if (options.cancel) {
                    return fn2Result;
                }

                fn1Result = fn1.apply(this, (options.overrideArgs ? fn2Result : arguments));

                return options.overrideReturn ? fn2Result : fn1Result;
            };
        },
        after: function (fn1, fn2) {
            return function () {
                var fn1Result = fn1.apply(this, arguments),
                    options = injectNewOptions(arguments, Options),
                    fn2Result;

                options.result = fn1Result;

                fn2Result = fn2.apply(this, arguments);

                return options.overrideReturn ? fn2Result : fn1Result;
            };
        }
    },
        Options = {
            overrideReturn: false
        },
        BeforeOptions = Object.create(Options, {
            cancel: {
                enumerable: true,
                writable: true,
                value: false
            },
            overrideArgs: {
                enumerable: true,
                writable: true,
                value: false
            }
        });

    function injectNewOptions(args, baseOptions) {
        var options = Object.create(baseOptions);

        Array.prototype.push.call(args, options);

        return options;
    }

    inject = function (o, rx, pointcut, advice, maxDepth, depth) {
        var k, f;

        maxDepth = maxDepth || 0;
        depth = 0 || depth;

        for (k in o) {
            if (typeof (f = o[k]) === 'function' && rx.test(k)) {
                o[k] = pointcuts[pointcut](f, advice, pointcut);
            } else if (typeof f === 'object' && maxDepth <= depth) {
                inject(f, rx, pointcut, advice, maxDepth, ++depth);
            }
        }
    };

    inject.apply(this, arguments);
}
var o = {
    sum: function (a, b) {
        return a + b;
    },
    product: function (a, b) {
        return a * b;
    }
};

inject(o, /^sum$/, 'before', function () {
    var options = arguments[arguments.length - 1]; //get options object

    //override the arguments passed to the intercepted method
    options.overrideArgs = true; 

    return [2, 2];
});


inject(o, /^product$/, 'after', function () {
    var options = arguments[arguments.length - 1]; //get options object

    //override the arguments passed to the intercepted method
    options.overrideReturn = true; 

    return options.result + 3;
});

o.sum(1, 2); //4 because we have overriden the args with [2, 2]
o.product(2, 2); //7 because we added 3 to the result and overrided the return value