JavaScript中的Javascript模块

好的问题很简单。 我想使用两个JavaScript文件:一个在模块模式之后,另一个在调用第一个。 我使用Node.jstesting了所有的代码 当所有的代码在一个文件中的作品,但如果我把代码分成两个文件,我得到一个错误。

代码如下:

// module.js var testModule = (function(){ "use strict"; var counter = 0; return { incrementCounter : function(){ return counter++; }, resetCounter : function(){ console.log("Last Counter Value before RESET :" + counter); counter = 0; }; }; })(); // script.js var testModule = require('./module.js'); // i'm not sure about require or import "use strict"; testModule.incrementCounter(); testModule.resetCounter(); 

PS:我想用Javascript符号而不是Node导出符号来实现这个模式。

我将首先说不清楚为什么要使用Node.js中的“模块模式”。 我的意思是,你所build议的模块模式在客户端JavaScript,即浏览器中的JavaScript中更有意义,但是如果你的代码是在Node.js中运行,那么你可以利用已经存在于Node中的模块function在这种情况下,我个人认为在强制使用模块模式方面没有任何价值。

因此,我将首先以不同的方式深入研究使用Node.js模块模式,最后我将解释如何将“模块模式”和“Node.js模块模式”结合起来。

在模块,导入和导出

让我们从最明显,最简单的事情开始。 从Node工作的第一天起,可能每个人都在学习:每个代码文件都被认为是一个模块。 我们在其中声明的variables,属性,函数,构造函数对模块是私有的,其他模块不能访问它们或使用它们,除非模块的程序员明确暴露给公众; 也就是说我们在模块中声明的所有内容都是封装的,默认情况下是隐藏的,除非明确指出。 为了暴露一些程序员可以访问一个名为module的特殊对象,它有一个名为exports的特殊属性。 您在module.exports对象中发布的所有内容都可以被其他模块公开使用。 例如,在下面的代码中,除了foo.js ,其他任何模块都无法访问variablespi ,而名为bar的属性可以被任何其他导入模块foo.js的模块公开使用。 请注意,这与Node.js中的JavaScript与JavaScript中的JavaScript进行比较时的基本区别,JavaScript在浏览器中执行,JavaScript文件中的函数可能会在全局对象(即window )中公开window

 //module foo.js var pi = 3.14; module.exports.bar = 'Hello World'; 

现在,第二个模块baz.js可以“导入”模块foo.js并访问属性bar 。 在Node中,我们通过使用名为require的全局函数来实现这个效果。 有点如下:

 //module baz.js var foo = require('./foo'); console.log(foo.bar); //yields Hello World 

技术1 – 扩展具有附加function的导出对象

因此,在模块中公开function的一种技术是在module.exports对象中添加函数和属性。 在这种情况下,Node提供对exports对象的直接访问,以使我们更简单。 例如:

 //module foo.js exports.serviceOne = function(){ }; exports.serviceTwo = function(){ }; exports.serviceThree = function(){ }; 

正如你所期望的那样,这个模块的用户在导入它的时候会获得一个对这个exports对象的引用,这样他们就可以访问它所暴露的所有function。

 //module bar.js var foo = require('./foo'); foo.serviceOne(); foo.serviceTwo(); foo.serviceThree(); 

技术2 – 替代默认导出对象与另一个对象

在这一点上,你可能会怀疑,由于module.exports只是一个暴露了模块的公共部分的对象,所以我们可以定义我们自己的对象,然后用我们自己的replace默认的module.exports对象。 例如:

 //module foo.js var service = { serviceOne: function(){ }, serviceTwo: function(){ }, serviceThree = function(){ } }; module.exports = service; 

最后一个例子中的代码的行为与上例中的代码完全一样,只是这次我们明确地创build了导出的对象,而不是使用Node默认提供的那个。

技术3 – 使用构造函数replace默认导出对象

在迄今为止的例子中,我们总是使用一个对象的实例作为我们暴露的目标。 然而,在某些情况下,允许用户根据需要创build给定types的多个实例似乎更为方便。 没有什么能阻止我们用其他types的对象(比如构造函数)replacemodule.exports对象。 在下面的例子中,我们公开了一个构造函数,用户可以使用它来创build许多Footypes的实例。

 //module Foo.js function Foo(name){ this.name = name; } Foo.prototype.serviceOne = function(){ }; Foo.prototype.serviceTwo = function(){ }; Foo.prototype.serviceThree = function(){ }; module.exports = Foo; 

而这个模块的用户可以简单地做这样的事情:

 //module bar.js var Foo = require('./Foo'); var foo = new Foo('Obi-wan'); foo.serviceOne(); foo.serviceTwo(); foo.serviceThree(); 

技术4 – 使用普通旧function替代默认导出对象

现在很容易想象,如果我们可以使用构造函数,那么我们也可以使用任何其他普通的旧JavaScript函数作为module.exports公开的目标。 正如在下面的例子中,我们的导出函数允许这个模块的用户访问其他封装的服务对象之一。

 //foo.js var serviceA = {}; serviceA.serviceOne = function(){ }; serviceA.serviceTwo = function(){ }; serviceA.serviceThree = function(){ }; var serviceB = {}; serviceB.serviceOne = function(){ }; serviceB.serviceTwo = function(){ }; serviceB.serviceThree = function(){ }; module.exports = function(name){ switch(name){ case 'A': return serviceA; case 'B': return serviceB; default: throw new Error('Unknown service name: ' + name); } }; 

现在导入这个模块的用户会收到我们上面声明的匿名函数的引用,然后她可以简单地调用这个函数来访问我们的一个封装对象。 例如:

 //module bar.js var foo = require('./foo'); var obj = foo('A'); obj.serviceOne(); obj.serviceTwo(); obj.serviceThree(); 

许多程序员通常调用由require返回的函数,而不是先将其分配给引用。 例如:

 //module bar.js var foo = require('./foo')('A'); foo.serviceOne(); foo.serviceTwo(); foo.serviceThree(); 

所以module.exports ,就像下面这样简单:我们在module.exports公开的所有东西都是我们在调用require时得到的。 使用不同的技术,我们可以暴露对象,构造函数,属性等。

基于所有这些例子,我说在代码中使用模块模式是没有意义的。

使用模块模式和Node.js模块

但是,如果你要创build一个你想在Node.js和浏览器中使用的库,那么使用这两种模式是有道理的。 虽然这在你的问题中并不明显。 但是,如果是这样的话,那么你可以把这两个想法结合在一起。

例如,做这样的事情:

 var TestModule; (function (TestModule) { var counter = 0; TestModule.incrementCounter = function(){ return counter++; }; TestModule.resetCounter = function(){ console.log('Last Counter Value before RESET :' + counter); counter = 0; }; return TestModule; })(typeof module === 'undefined' ? (TestModule || (TestModule = {})) : module.exports); 

在Node.js中运行时,在IIFE内 , TestModule对应于module.exports对象,当在浏览器中运行时, TestModule表示一个名称空间。

所以,如果你在Node中运行,你可以这样做:

 var testModule = require('./testModule'); testModule.incrementCounter(); 

如果您正在浏览器中运行,那么在加载此脚本之后,您可以直接访问命名空间TestModule。

 TestModule.incrementCounter();