模块化客户端Javascript的方法没有命名空间污染

dgh*_*dgh 10 javascript node.js

我正在编写客户端代码,并希望编写多个可以交互的模块化JS文件,同时防止全局命名空间污染.

的index.html

<script src="util.js"></script>
<script src="index.js"></script>
Run Code Online (Sandbox Code Playgroud)

util.js中

(function() {
    var helper() {
        // Performs some useful utility operation
    }
});
Run Code Online (Sandbox Code Playgroud)

index.js

(function () {
    console.log("Loaded index.js script");
    helper();
    console.log("Done with execution.");
})
Run Code Online (Sandbox Code Playgroud)

此代码很好地将实用程序函数保存在单独的文件中,并且不会污染全局命名空间.但是,不会执行辅助实用程序函数,因为'helper'存在于单独的匿名函数名称空间中.

一种替代方法涉及将所有JS代码放在一个文件中或在全局命名空间中使用单个变量,如下所示:

var util_ns = {
    helper: function() {
        // Performs some useful utility operation.        
    },
    etc.
}
Run Code Online (Sandbox Code Playgroud)

这两种方法在模块化和干净的命名空间方面都有缺点.

我习惯在Node.js中工作(服务器端),我可以在另一个内部"需要"一个Javascript文件,有效地将util.js绑定注入index.js命名空间.

我想在这里做类似的事情(但是客户端),允许代码在单独的模块化文件中编写,而不在全局命名空间中创建任何变量,同时允许访问其他模块(例如实用程序模块).

这是否可以通过简单的方式实现(没有库等)?

如果没有,在使客户端JS的行为更像Node和npm的领域,我知道requireJS,browserify,AMD和commonJS标准化尝试的存在.但是,我不确定每个的优缺点和实际用法.

Eug*_*nov 17

我强烈建议你继续使用RequireJS.


模块支持方法(不需要/依赖):

// moduleA.js

var MyApplication = (function(app) {

    app.util = app.util || {};

    app.util.hypotenuse = function(a, b) {
        return Math.sqrt(a * a + b * b);
    };

    return app;
})(MyApplication || {});

// ----------

// moduleB.js

var MyApplication = (function(app) {

    app.util = app.util || {};

    app.util.area = function(a, b) {
        return a * b / 2;
    };

    return app;
})(MyApplication || {});

// ----------

// index.js - here you have to include both moduleA and moduleB manually
// or write some loader

var a = 3,
    b = 4;
console.log('Hypotenuse: ', MyApplication.util.hypotenuse(a, b));
console.log('Area: ', MyApplication.util.area(a, b));
Run Code Online (Sandbox Code Playgroud)

在这里,您只创建一个全局变量(命名空间)MyApplication,所有其他内容都"嵌套"到其中.

小提琴 - http://jsfiddle.net/f0t0n/hmbb7/


**我之前在项目中使用过的另一种方法 - https://gist.github.com/4133310 但是无论如何我在开始使用RequireJS时抛弃了所有这些东西.*


Lan*_*nbo 7

您应该查看browserify,它将模块化JavaScript项目处理为单个文件.您可以require像在节点中一样使用它.

它甚至给出了一堆node.js libs url,httpcrypto.

附加:在我看来,浏览器的专业是它只是使用并且不需要自己的代码 - 您甚至可以使用已经编写的node.js代码.根本没有必要的样板代码或代码更改,它与node.js一样符合CommonJS标准.它输出一个.js允许您require在网站代码中使用的单个.

这有两个缺点,恕我直言:首先是浏览器编译的两个文件可以覆盖它们的require功能,如果它们包含在相同的网站代码中,那么你必须要小心.另一个当然是每次都要运行browserify来更改代码.当然,模块系统代码始终是编译文件的一部分.


Rob*_*obG 6

所谓的"全球名称空间污染"被大大超过了一个问题.我不知道node.js,但在一个典型的DOM中,默认情况下有数百个全局变量.名称重复很少是明智地选择名称的问题.添加一些使用脚本不会产生丝毫差异.使用如下模式:

var mySpecialIdentifier = mySpecialIdentifier || {};
Run Code Online (Sandbox Code Playgroud)

意味着添加一个可以作为所有代码的根的变量.然后,您可以添加模块到您的内容,例如

mySpecialIdentifier.dom = {
    /* add dom methods */
}
(function(global, undefined) {
    if (!global.mySpecialIdentifier) global.mySpecialIdentifier = {};
    /* add methods that require feature testing */
}(this));
Run Code Online (Sandbox Code Playgroud)

等等.

您还可以使用"扩展"功能来测试和添加基础对象,这样您就不会复制该代码,并且可以轻松地从不同文件向基础库对象添加方法.您的库文档应该告诉您是否在它成为问题之前复制名称或功能(并且测试也应该告诉您).

您的整个库可以使用单个全局变量,并且可以根据需要轻松扩展或修剪.最后,您不依赖任何第三方代码来解决相当微不足道的问题.


ale*_*gui 6

我强烈建议您尝试构建工具.

构建工具将允许您在开发时使用不同的文件(即使在不同的文件夹中),并在最后将它们连接起来以进行调试,测试或生产.更好的是,您不需要向项目添加库,构建工具驻留在不同的文件中,并且不包含在您的发行版中.

我使用GruntJS,基本上就是这样的.假设您在js目录中都有您的util.jsindex.js(需要定义辅助对象).您可以单独开发它们,然后将它们连接到将由html加载的dist目录中的app.js文件.在Grunt中,您可以指定以下内容:

concat: {
    app: {
        src: ['js/util.js', 'js/index.js'],
        dest: 'dist/app.js'
    }
}
Run Code Online (Sandbox Code Playgroud)

这将自动创建文件的串联.此外,您可以缩小它们,拖拽它们,并进行任何您想要的过程.您也可以将它们放在完全不同的目录中,最后仍然会以正确的顺序将一个文件与您的代码打包在一起.您甚至可以在每次保存文件时触发该过程以节省时间.

最后,从HTML中,您只需要引用一个文件:

<script src="dist/app.js"></script>
Run Code Online (Sandbox Code Playgroud)

添加驻留在不同目录中的文件非常简单:

concat: {
    app: {
        src: ['js/util.js', 'js/index.js', 'js/helpers/date/whatever.js'],
        dest: 'dist/app.js'
    }
} 
Run Code Online (Sandbox Code Playgroud)

而你的html仍然只会引用一个文件.

BrunchYeoman也是其他一些可用的工具.

--------编辑-----------

需要JS(和一些替代品,如Head JS)是一个非常流行的AMD(异步模块定义),它允许简单地指定依赖项.另一方面,构建工具(例如,Grunt)允许管理文件并添加更多功能而不依赖于外部库.在某些情况下,你甚至可以使用两者.

我认为将文件依赖项/目录问题/构建过程与代码分开是可行的方法.使用构建工具,您可以清楚地看到代码,并在一个完全独立的位置指定如何处理文件.它还提供了一个非常可扩展的体系结构,因为它可以通过结构更改或将来的需求(例如包括LESS或CoffeeScript文件)来完成.

最后一点,在生产中使用单个文件也意味着更少的HTTP开销.请记住,尽量减少对服务器的调用次数非常重要.拥有多个文件效率非常低.

最后,这是一篇关于AMD工具构建工具的精彩文章,值得一读.