使用 require 的 Node.js 命名空间

8

我正在玩弄和学习使用vows做个人项目。这是一个小的客户端库,测试是用vows完成的。因此,我必须构建和测试一个像这样编写的文件:

(function(exports) { 
    var module = export.module = { "version":"0.0.1" }; 
    //more stuff
})(this);

在我的测试中(基于science.js、d3等),需要这样的模块:

require("../module");

当我尝试运行测试时,我不断收到“模块未定义错误”的提示,因此我去了一个 repl 并运行了以下命令:

require("../module")

并且它返回了:

{ module: { version: "0.0.1" } }

我意识到我可以做类似以下的操作:
var module = require("../module").module;

我觉得用这种方式创建问题,尤其是因为我所依赖的库都是按照我描述的格式进行操作。

我希望我的项目能够与我所基于的项目类似,其中:

require("../module");

在此命名空间中创建一个变量:
module.version; //is valid.

我在许多库中都看到过这个,我按照格式和思路进行操作,但我相信我可能缺少关于require行为的一些了解。


相关链接:Node.js 命名空间 - blong
1个回答

18

这种方式创建没有问题。模块定义了module.exports对象中的返回内容。顺便说一下,你实际上不需要自执行函数(SEF),它不会像浏览器中那样造成全局泄漏。

示例

module1.js:

module.exports = {
    module: { 'version': '0.1.1' }
};

main.js:

var module1 = require( './module1.js' );
// module1 has what is exported in module1.js

一旦你理解了这个过程,如果你想要的话,你可以轻松地立即导出版本号:
module1.js:
module.exports = '0.1.1';

main.js:

var module1 = require( './module1.js' );
console.log( module1 === '0.1.1' ); // true

或者如果你需要一些逻辑处理的话,你可以像这样简单地扩展你的 module1.js 文件:

module.exports = ( function() {
    // some code
    return version;
} () ); // note the self executing part :-)
// since it's self executed, the exported part
// is what's returned in the SEF

或者,就像许多模块一样,如果您想要导出一些实用函数(并保持其他函数“私有”),您可以这样做:
module.exports = {
    func1: function() {
        return someFunc();
    },

    func2: function() {},

    prop: '1.0.0'
};

// This function is local to this file, it's not exported
function someFunc() {
}

所以,在 main.js 中:
var module1 = require( './module1.js' );
module1.func1(); // works
module1.func2(); // works
module1.prop; // "1.0.0"
module1.someFunc(); // Reference error, the function doesn't exist

您的特殊情况

关于您的特殊情况,我不建议像他们那样做。

如果您在这里看一下:https://github.com/jasondavies/science.js/blob/master/science.v1.js

您会发现他们没有使用var关键字。因此,他们创建了一个全局变量

这就是为什么一旦他们require定义全局变量的模块后,就可以访问它。

顺便说一句,在他们的情况下,exports参数是无用的。甚至是误导性的,因为它实际上是global对象(相当于浏览器中的window),而不是module.exports对象(函数中的this是全局对象,如果启用了严格模式,则为undefined)。

结论

不要像他们那样做,这是个坏主意。全局变量是个坏主意,最好使用节点的哲学,并将所需模块存储在可重复使用的变量中。

如果您想在客户端使用并在node.js中进行测试的对象,则可以这样做:

yourModule.js:

// Use either node's export or the global object in browsers
var global = module ? module.exports : window.yourModule;

( function( exports ) {
    var yourModule = {};
    // do some stuff
    exports = yourModule;
} ( global ) );

您可以采用以下方式缩短代码,以避免创建全局变量:global
( function( exports ) {
    var yourModule = {};
    // do some stuff
    exports = yourModule;
} ( module ? module.exports : window.yourModule ) );

这样,您就可以在客户端使用它,像这样:
yourModule.someMethod(); // global object, "namespace"

在服务器端:

var yourModule = require( '../yourModule.js' );
yourModule.someMethod(); // local variable :-)

仅供参考,.. 表示 "上级目录"。这是获取模块的相对路径。如果文件与当前目录在同一级别,则使用 .


谢谢Florian,这解决了很多有关Node处理require和模块命名空间的问题。 - Miles McCrocklin
尽管我正在创建一个客户端库,并使用node中的vows进行单元测试,但这是一个客户端库。话虽如此,我认为SEF是必需的,但当我包含SEF时,它会使require(“../module)返回{module:{“stuff”:“value”}},所以我使用了node自己的管理器来处理命名空间问题,因此我的模块似乎变得相对无用。我基于以下测试方法进行了测试:science.js、d3.js和dc.js。与其在所有测试中将变量设置为exports:var d3 = require(“d3”);,它使用require(“d3”);并直接调用d3。 - Miles McCrocklin
我不知道为什么会出现这种行为,尽管它似乎非常适合用于使用Node构建、测试和维护客户端库。我不知道发生了什么,如果您能看一下,我会非常感激。如果不能,非常感谢您肯定解决了我在处理和创建服务器端模块方面遇到的一些问题。 - Miles McCrocklin
如果您喜欢这个答案,请点赞! :) 不过,我不明白您在评论中所说的意思。如果您觉得需要更详细地说明您的问题,不要犹豫,编辑您的问题即可。 - Florian Margaine
@MilesMcCrocklin 我编辑了我的答案,在结尾处添加了关于你特殊情况的解释 :) - Florian Margaine

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接