在预先存在的命名空间中定义所有外部 JavaScript

Define all external JavaScript in a pre-existing namespace

本文关键字:定义 外部 JavaScript 命名空间 存在      更新时间:2023-09-26

我完全意识到 eval 等的危险,所以请不要浪费时间评论这些黑客是如何被黑客攻击的。

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

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

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

基本上,我想实现如果我将 library.js 的内容粘贴到我的 Bar 命名空间中会得到的结果,一个 la:

var Bar = {
    x: 0,
    foo: function() {}
  }

但是,当我也无法控制Bar命名空间时,我正在尝试执行此操作。因此,我无法"复制和粘贴"。另一个相关的例子是我们如何在 Ruby 中将新方法"打入"预先存在的类定义中。

最后,我的具体用例是我尝试将 p5.js 中的所有内容"注入"到 Opal 命名空间中。

适用于当今浏览器的简单解决方案是:

<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 模块。如果您使用 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 而想投反对票,请记住,无论他们是否将其放入 <script> 标签中,OP 都已经将执行脚本。

这是一个无需获取脚本的工作示例。

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()

在不污染全局作用域的情况下处理库和其他依赖项的一种方法是使用某种异步模块定义。我喜欢使用和推荐的一个很好的是 Require.js。

它背后的整个想法是,您可以将库和依赖项捆绑到模块中,并在需要时调用它们,而无需简单地在全局范围内加载所有内容。

下面是如何在您的情况下使用它的示例:

require(['http://example.com/library.js'], function(Bar){
    // library.js is now bound within this scope under the alias Bar
});