我有一个奇怪的问题,我需要将一些 javascript 注入到另一个 javascript 函数中。我正在使用一个已锁定的框架,因此我无法更改现有功能。
我得到的是这样的东西
函数 doSomething(){ ... }...***
我可以操纵 ***(上面),但是我无法更改 doSomething 函数...相反,我需要以某种方式将几行代码注入到 doSomething 代码的末尾。
我需要这样做的原因是自定义框架调用 doSomething() ,这会导致我需要提取的服务器返回一个 ID。该 ID 仅在 doSomething 函数内部引用,因此除非我向该函数注入代码(除非我错过了某些内容),否则我无法捕获它。
有办法做到这一点吗?
别名。
var oldDoSomething = doSomething;
doSomething = function() {
// Do what you need to do.
return oldDoSomething.apply(oldDoSomething, arguments);
}
通过使用源代码字符串来更改函数可能非常简单。要针对特定实例执行此操作,请尝试:
eval(doSomething.toString().replace(/}\s*$/, ' return id; $&'));
现在
doSomething
返回 ID。我通常不喜欢 eval
,但由于需要访问局部变量,普通的 面向方面编程 技术并不适用于此。
如果
doSomething
已经返回值,请尝试将主体包装在 try ... finally
:
eval(doSomething.toString()
.replace(/^function *\w* *\([^)]*\) *{/, '$& try {')
.replace(/}\s*$/, '} finally { window.someID = id; } $&')
);
要将其转换为函数,我们需要使代码在全局范围内评估。最初,这个答案使用
with
来更改 eval
的范围,但这目前在浏览器中不起作用。相反,.call
用于将 eval
的范围更改为 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; }');
如果要重写绑定到变量的方法和匿名函数,请将
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;');
注意函数的第一个参数现在是字符串。可以进行进一步的改进来处理在全局范围内无法访问的方法。
无需更改
doSomething
,而是可以包装它调用的函数,这些函数提供 doSomething
用于生成 ID 的数据(或者一般来说,要捕获的任何数据)。有一些注意事项:
doSomething
,这可能会引入错误。doSomething
必须处理结果,则包装器必须重复此处理(这违反了 DRY 原则)。如果处理量很小,这不会是一个大问题,但仍然是潜在的错误来源(特别是,如果 doSomething
中的处理发生变化但包装器没有变化,代码可能会不同步) .感谢您的所有反馈。每个答案都给了我一个线索,因此我提出了以下解决方案。
<body>
<script type="text/javascript">
var val;
function doSomething(item1, item2) {
var id = 3;
}
function merge() {
var oScript = document.createElement("script");
var oldDoSomething = doSomething.toString();
oScript.language = "javascript";
oScript.type = "text/javascript";
var args = oldDoSomething.substring(oldDoSomething.indexOf("(") + 1, oldDoSomething.indexOf(")"));
var scr = oldDoSomething.substring(oldDoSomething.indexOf("{") + 1, oldDoSomething.lastIndexOf("}") - 1);
var newScript = "function doSomething(" + args + "){" + scr + " val = id; }";
oScript.text = newScript;
document.getElementsByTagName('BODY').item(0).appendChild(oScript);
}
merge();
</script>
<input type="button" onclick="doSomething();alert(val);" value="xx" />
我不确定你所说的“锁定”是什么意思 - JavaScript 由你的浏览器解释。即使它已被缩小或编码,您仍然可以从脚本页面的源代码中复制该函数的源代码。此时,您将原始函数重新分配给您自己的设计之一,其中包含复制的代码以及您自己的代码。
var id;
var myFunction = function() {
// code copied from their source, which contains the variable needed
// insert your own code, such as for copying the needed value
id = theirIDvalue;
// return the value as originally designed by the function
return theirValue;
};
theirObject.theirFunction = myFunction;
function doSomething() { ... }
var oldVersionOfFunc = doSomething;
doSomething = function() {
var retVal = oldVersionOfFunc.apply(oldVersionOfFunc, args);
// your added code here
return retVal;
}
这似乎会引发错误(递归太多)
function doSomething(){ document.write('Test'); return 45; }
var code = 'myDoSomething = ' + doSomething + '; function doSomething() { var id = myDoSomething(); document.write("Test 2"); return id; }';
eval(code)
doSomething();
这对我有用,即使它很丑。
函数是 Javascript 中的第一类值,这意味着您可以将它们存储在变量中(事实上,声明命名函数本质上是将匿名函数分配给变量)。
你应该能够做这样的事情:
function doSomething() { ... }
var oldVersionOfFunc = doSomething;
doSomething = function() {
var retVal = oldVersionOfFunc.apply(oldVersionOfFunc, args);
// your added code here
return retVal;
}
(但是,我可能是错的。我的 JavaScript 有点生疏。;))
我无法更改 doSomething 函数...相反,我需要以某种方式在 doSomething 代码的末尾注入几行代码
对我来说,在
doSomething
代码末尾插入几行听起来就像更改 doSomething
函数。不幸的是,我认为你完蛋了。
(我对这一切都有点模糊,但我认为函数是那些担心这些事情的人如何在 JavaScript 中实现信息隐藏的,正是因为你无法从它们外部访问它们的作用域。)