将javascript注入javascript函数

Gru*_*unt 8 javascript function code-injection

我有一个奇怪的问题,我需要将一些javascript注入另一个javascript函数.我正在使用一个锁定的框架,所以我无法更改现有的功能.

我得到的是这样的

function doSomething(){...}

...***

我可以操纵***(上面)但是我不能改变doSomething函数...相反,我需要以某种方式在doSomething代码的末尾注入几行代码.

我需要这样做的原因是自定义框架调用doSomething(),这导致从我需要提取的服务器返回一个ID.这个ID只在doSomething函数中引用,所以我无法捕获它,除非我向该函数注入代码(除非我遗漏了一些东西).

有没有办法做到这一点?

Aug*_*aas 6

别名吧.

var oldDoSomething = doSomething;
doSomething = function() {
  // Do what you need to do.
  return oldDoSomething.apply(oldDoSomething, arguments);
}
Run Code Online (Sandbox Code Playgroud)

  • 他需要访问范围为原始函数的变量 (2认同)

out*_*tis 6

通过使用源代码字符串来更改函数可能非常简单。要针对特定​​实例执行此操作,请尝试:

eval(doSomething.toString().replace(/}\s*$/, ' return id; $&'));
Run Code Online (Sandbox Code Playgroud)

现在doSomething返回 ID。我通常不喜欢eval,但由于需要访问局部变量,普通的面向方面的编程技术在这里不适用。

如果doSomething已经返回一个值,请尝试将主体包装在try ... finally

eval(doSomething.toString()
    .replace(/^function *\w* *\([^)]*\) *{/, '$& try {')
    .replace(/}\s*$/, '} finally { window.someID = id; } $&')
);
Run Code Online (Sandbox Code Playgroud)

要将其转换为函数,我们需要使代码在全局范围内计算。最初,此答案用于with更改 的范围eval,但这目前在浏览器中不起作用。相反,.call用于更改evalto的范围window

(function () {
    var begin = /^function\s*\w*\s*\([^)]*\)\s*{/,
        end = /}\s*$/;

    function alter(func, replacer) {
        var newFunc = replacer(func.toString());
        eval.call(window, newFunc);
    }
    
    function insertCode(func, replacer, pattern) {
        alter(func, function (source) { 
            return source.replace(pattern, replacer);
        });
    };
    
    /* Note: explicit `window` to mark these as globals */
    window.before = function (func, code) {
        return insertCode(func, '$& ' + code, begin);
    };
    
    window.after = function (func, code) {
        return insertCode(func, code + ' $&', end);
    };
    
    window.around = function (func, pre, post) {
        /* Can't simply call `before` and `after`, as a partial code insertion may produce a syntax error. */
        alter(func, function(source) {
            return source
                .replace(begin, '$& ' + pre)
                .replace(end, post + ' $&');
        });
    };
})();
...
after(doSomething, 'return id;');
/* or */
around(doSomething, 'try {', '} finally { window.someID = id; }');
Run Code Online (Sandbox Code Playgroud)

如果要重写与变量绑定的方法和匿名函数,请更改alter为:

...
    function alter(func, replacer) {
        var newFunc = replacer(eval('window.' + funcName).toString());
        eval.call(window, newFunc);
    }
...
function Foo() {}
Foo.prototype.bar = function () { var secret=0x09F91102; }
...
after('Foo.prototype.bar', 'return secret;');
Run Code Online (Sandbox Code Playgroud)

请注意,函数的第一个参数现在是字符串。可以进行进一步的改进来处理在全局范围内无法访问的方法。

替代方法

可以不改变doSomething,而是包装它调用的函数,这些函数提供doSomething用于生成 ID 的数据(或者一般来说,要捕获的任何数据)。有一些注意事项:

  • 被调用的函数只有在全局范围内才能被包装,或者代码可以在其定义的范围内注入和运行。
  • 包装该函数将影响所有使用它的代码,而不仅仅是doSomething,这可能会引入错误。
  • 如果被调用的函数不直接返回感兴趣的数据而是doSomething必须处理结果,则包装器必须重复此处理(这违反了 DRY 原则)。如果处理量很小,这不会是一个大问题,但仍然是潜在的错误来源(特别是,如果处理更改doSomething但包装器未更改,则代码可能会不同步)。


Amb*_*ber 0

函数是 Javascript 中的第一类值,这意味着您可以将它们存储在变量中(事实上,声明命名函数本质上是将匿名函数分配给变量)。

你应该能够做这样的事情:

function doSomething() { ... }

var oldVersionOfFunc = doSomething;
doSomething = function() {

    var retVal = oldVersionOfFunc.apply(oldVersionOfFunc, args);

    // your added code here

    return retVal;
}
Run Code Online (Sandbox Code Playgroud)

(但是,我可能是错的。我的 javascript 有点生疏。;))