我完全了解评估等等的危险,所以请不要浪费时间评论这些黑客是如何破解的。
假设我正在使用第三方JavaScript库,但我希望所有代码都在特定的命名空间中定义...我也无法控制。例如:
<script src="http://example.com/library.js>
<!-- library.js contains:
var x = 0;
function foo() {}
-->
我希望能够在名称空间x
的上下文中定义foo
和Bar
,而不是全局上下文(window
)。
假设Bar
已定义,但在另一个我无法控制的外部js文件中,我尝试了一些黑客攻击,例如:
<script>
var externalCode = $.ajax("http://example.com/library.js");
eval.call(Bar, externalCode);
// and also tried:
(function(str){ eval(str); }).call(Bar, externalCode);
</script>
但我无法访问Bar.x
或调用Bar.foo()
。
基本上,如果我在我的Bar
命名空间中粘贴了 library.js 的内容,我希望得到的结果是la:
var Bar = {
x: 0,
foo: function() {}
}
但是,当我也无法控制Bar
命名空间时,我正在尝试这样做。因此,我不能“复制和粘贴”。另一个相关的例子是我们如何将新方法“插入”Ruby中预先存在的类定义。
最后,我的具体用例是我试图将 p5.js 中的所有内容“注入”Opal
命名空间。
答案 0 :(得分:3)
今天在浏览器中运行的简单解决方案是:
<script src="http://example.com/library.js>
<!-- library.js contains:
var x = 0;
function foo() {}
-->
<script>
$.ajax('/library.js', function(jsString) {
var wrapped = `function() {
${jsString};
Bar.x = x;
Bar.foo = foo;
}();`
eval(wrapped);
});
</script>
但是,不需要列出模块的解决方案是EcmaScript 6 modules。如果您使用https://babeljs.io/进行转换,则可以使用今天的浏览器。
//------ main.js ------
import * as Bar from 'lib';
console.log(Bar.foo()); // no error
console.log(Bar.x); // 0
如果您今天需要在所有浏览器中使用它,并且不想转换代码,那么您需要第一个示例,可以概括为
function importIntoNs(ns, scriptUrl, symbolNames) {
$.ajax(scriptUrl, function(jsString) {
var wrapped = `(function() {
${jsString};`;
symbolNames.forEach(symbolName => {
wrapped += 'this.' + symbolName + ' = symbolName;\n' ;
});
wrapped += '})';
eval(wrapped).apply(ns);
}
}
importIntoNs(Bar, 'http://example.com/library.js', ['x', 'foo']);
如果有人因为eval而想要进行投票,请记住OP已经开始执行该脚本,无论他们将它放在<script>
标签中。
这是一个没有提取脚本的工作示例。
function importScriptIntoNs(ns, jsString, symbolNames) {
var addSymbols = '';
symbolNames.forEach(symbolName => {
addSymbols += 'this.' + symbolName + ' = ' + symbolName + ';\n';
});
var wrapped = `(function() {;
${jsString}
${addSymbols}
})`;
eval(wrapped).apply(ns);
}
var Bar = {};
var jsString = `
var x = 'It works';
function foo() {alert(x)}
`;
importScriptIntoNs(Bar, jsString, ['x', 'foo']);
Bar.foo()
&#13;
答案 1 :(得分:1)
处理库和其他依赖项而不污染全局范围的一种方法是使用某种Asynchronous Module Definition。我喜欢使用和推荐的一个好的是Require.js。
它背后的整个想法是,您可以将库和依赖项捆绑到模块中,并在需要时调用它们,而无需简单地在全局范围内加载所有内容。
以下是在您的情况下如何使用它的示例:
require(['http://example.com/library.js'], function(Bar){
// library.js is now bound within this scope under the alias Bar
});