在预先存在的命名空间

时间:2016-03-11 20:48:14

标签: javascript

我完全了解评估等等的危险,所以请不要浪费时间评论这些黑客是如何破解的。

假设我正在使用第三方JavaScript库,但我希望所有代码都在特定的命名空间中定义...我也无法控制。例如:

<script src="http://example.com/library.js>
<!-- library.js contains:
     var x = 0;
     function foo() {}
-->

我希望能够在名称空间x的上下文中定义fooBar,而不是全局上下文(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命名空间。

2 个答案:

答案 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>标签中。

这是一个没有提取脚本的工作示例。

&#13;
&#13;
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;
&#13;
&#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
});