在私有范围内包含JS文件

时间:2011-02-14 22:43:45

标签: javascript

我只是希望能够包含JS文件,但不能在全局范围内对其进行评估。

我已经浏览了labjs和requirejs,虽然他们可以做1000件其他事情但似乎都没能解决这个问题。

我知道我可以包装foo.js的代码,以便它需要某个上下文,并且可以在其中执行,但这不是我正在寻找的(必须更改源文件) 。相反,我希望源JS文件保留为任何其他JS文件,不需要任何类型的元数据或通过包含文件本身中的代码解析运行时执行上下文;在包含文件之外,没关系。

为清晰起见,进行了一些简单的演示:

/*
 * Example 1 - apply to current context
 */
function x() {  
    include('foo.js'); // provides foo()
    foo(); // ok!
}
foo(); // not ok!


/*
 * Example 2 - apply to namespace context
 */
include.apply(ns, ['foo.js']); // provides foo()
ns.foo(); // ok!
foo(); // not ok!

我知道可以使用eval()或使用字符串创建new Function来实现这一点,但我希望有更好的解决方案。

4 个答案:

答案 0 :(得分:3)

没有 clean 方法可以做到这一点而不依赖于包含的源文件符合某种模式(我可以想到注入每个文件的可能性)进入自己的iframe,所以他们用自己的window运行,但我认为这不是理想的。)

这并不一定意味着你无法实现所包含文件的解耦(如果没有意识到运行时上下文),但文件仍然需要表现良好。

CommonJS模块系统(RequireJS supports)显示了实现此目的的一种方法。通过将代码包装在提供exports对象的函数中,并将所有(先前)全局属性分配给此对象,RequireJS可以返回此对象,以便分配给任何范围内的任何对象。

例如:

function x() {  
    var foo = require('foo.js').foo; // provides foo()
    foo(); // ok!
}
foo(); // not ok!

var ns = require('foo.js');
ns.foo(); // ok!
foo(); // not ok!

是的,这需要编辑源文件,但它仍然提供您所需的好处。

答案 1 :(得分:3)

我认为不可能以你自己概述的方式这样做。

一种解决方案是将代码包装在服务器端的函数中,并编写一个include函数来加载js文件并使用函数的命名空间进行响应。我想到了一个类似于node.js中的require函数的解决方案。

服务器端的已包装代码

require.response('foo', function(exports) {

    // Content in foo.js
    exports.a = function() { return 'A'; }
    this.b = function() { return 'B'; }
    function c() { return 'c'); }

});

客户端js:

window['require'] = (function() {

    var list = {};
    function require(name, cb) {
        if (list[name]) {
            if (list[name].ns) {
                cb(list[name].ns);
            }
            else {
                list[name].cb.push(cb);
            }
        }
        else {
            list[name] = {cb: [cb]};

            // Code to load js file

        }
    }

    require.response = function(name, func) {
        var ns = {};
        list[name].ns = ns;
        func.call(ns);
        for(var i = 0, l = list[name].cb; i < l; i++) {
            list[name].cb[i](ns);
        }
    }

    return require;

}());

使用示例:

require('foo', function(foo) {
    foo.a(); // ok
    foo.b(); // ok
    foo.c(); // error
};

foo.a(); // error
this.a(); // error
c(); // error

答案 2 :(得分:1)

要结束这个老问题,遗憾的是没有找到可接受的解决方案。一些很好的建议,但都破坏了我的要求。我想这个语言的当前功能集可能是不可能的。

答案 3 :(得分:0)

clientside-require package使用iframe来包含加载的javascript的范围。

将脚本加载到您的页面后:

<script src = "path/to/clientside-require/src/index.js"></script>

您可以按照以下方式执行所需的功能:

/*
 * Example 1 - apply to current context
 */
require("foo.js")
  .then((foo)=>{
     foo(); // ok
  })
foo(); // not ok


/*
 * Example 2 - apply to namespace context
 */
var promise_foo_attached = require("foo.js")
  .then((foo)=>{
    window.ns.foo = foo; // provides foo to ns
  })

// then, as long as promise_foo_attached has resolved:
ns.foo(); // ok!
foo(); // not ok!

注意,因为浏览器不允许同步加载资源(由于糟糕的用户体验),任何使用javascript加载外部脚本的尝试都必然是异步的(因此使用promises)。

除非浏览器启用同步加载,否则此格式将最接近我们要查找的内容。

作为额外的好处,该脚本加载的require()功能可用于加载htmlcssjson,甚至node_modules(只要模块能够使用require()作为承诺)。