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许多Foo
types的实例。
//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();