我可以像这样模块化我的javascript代码吗?

时间:2015-05-29 10:58:16

标签: javascript performance scope

我真的不喜欢我的所有javascript代码只在一个文件中结束,我尝试使用像require.js这样的库来帮助我模块化,但它们最终让我有点复杂。所以我想知道我是否可以做这样的事情。

背景: 假设我想要一个不同的模块来处理随机数生成。我创建了一个新文件(首字母是我自制的约会)

// File : RandomNumberGenerator.js

(function(){
    // export this to the outer world
    _randomGenerator = new RandomNumberGenerator();

    function RandomNumberGenerator() {
    }

    RandomNumberGenerator.prototype.getRandomNumber = function(rangeLower, rangeHigher) {
        if(rangeLower !== undefined && rangeHigher === undefined) { // user passed in just one parameter
            rangeHigher = rangeLower;
            rangeLower = 0;
        }else if (rangeLower === undefined && rangeHigher === undefined) {
            return (Math.random());
        }

        return (Math.floor((Math.random() * (rangeHigher - rangeLower)) + rangeLower));
    };
})();

现在_randomGenerator对全局范围可见。我添加了' _'再次成为一个自制的会议。

现在我可以在我的主要js文件中使用它,如

(function() {
    console.log(_randomGenerator.getRandomNumber(5));
})();

我可以这样做吗?从长远来看,我有什么东西可以解决问题吗?通过做这样的事情是否有可能污染全球范围?

4 个答案:

答案 0 :(得分:2)

您所描述的是Immediately invoked function expression模块模式。

要导出需要将它们附加到全局window对象的函数和对象,通常建议您将它们放在命名空间下以避免与其他代码冲突:

(function() {

    function RandomNumberGenerator() {
    }

    RandomNumberGenerator.prototype.getRandomNumber = function(rangeLower, rangeHigher) {
        ...
    };

    // Create new namespace
    window.MyModule = {};

    // Export the RandomNumberGenerator
    window.MyModule.RandomNumberGenerator = RandomNumberGenerator;

    // Export a single instance
    window.MyModule.defaultRandomNumberGenerator = new RandomNumberGenerator();
})();

您还可以将值导入IIFE,从而可以创建别名:

(function(rng, max) {

    console.log(rng.getRandomNumber(max));

})(MyModule.defaultRandomNumberGenerator, 5);

我可以这样做吗?

从长远来看,我有什么东西可以忽视吗?

不要让你的IIFE变得太大,否则你将失去利益。小型明确定义的模块更易于维护。

此外,如果你想要两个模块共享同一个命名空间,你需要做一些比上面的例子更精细的事情,否则一个模块会覆盖另一个模块。

IIFE可以保持状态,但它们实际上是单身,试图确保你创建对象并将任何状态置于那些状态以避免global state

是否有可能通过这样做来污染全球范围?

是的,这是可能的;您仍然应该仔细命名您的命名空间 - 例如,不要使用window.jQuery

答案 1 :(得分:1)

是的,你可以这样做。事实上,这是一种非常频繁的模式。它通常与JS文件的自动连接相结合,保持源文件(非连接)和生产文件之间的区别(连接,并且通常也缩小)。

只是明确表示您正在创建窗口对象的属性。变化

_randomGenerator = new RandomNumberGenerator();

window._randomGenerator = new RandomNumberGenerator();

strict mode中必须使用此明确声明。

答案 2 :(得分:0)

虽然您可以模块化这样的代码,但您仍然可能会引入大量的全局变量。你正在使用一种防御策略,通过添加_前缀,但是根本不使用它们更安全。

您实际上只需要一个模块模式。只要你的第一个文件有这样的东西:

window.module = {};

然后其余模块可以将自己注册为模块对象的属性,而不必扭曲它们的名称。

module.randomNumberGenerator = new RandomNumberGenerator();

function RandomNumberGenerator() {
  ...
}    

然后在任何其他文件中,您可以使用该实例。

var rng = module.randomNumberGenerator;
// ...
var number = rng.getRandomNumber();

答案 3 :(得分:0)

这样可行,但它过于复杂,除非您声明了一些数据需要放入范围以保持隔离。

对于您的示例,您可以直接创建对象,而不是只使用构造函数来创建单个实例:

// File : RandomNumberGenerator.js

_randomGenerator = {

    getRandomNumber: function(rangeLower, rangeHigher) {
        if(rangeLower !== undefined && rangeHigher === undefined) { // user passed in just one parameter
            rangeHigher = rangeLower;
            rangeLower = 0;
        }else if (rangeLower === undefined && rangeHigher === undefined) {
            return (Math.random());
        }

        return (Math.floor((Math.random() * (rangeHigher - rangeLower)) + rangeLower));
    }

}

如果你有一个类,如果你没有任何静态数据,你仍然不需要将它包装在一个范围内:

function SomeClass() {
  // ...
}

SomeClass.prototype.someMethod = function(){
  // ...
}

如果您有类的静态数据,则可以使用范围,例如跟踪所有实例:

var SomeClass = (function(){

  var instances = [];

  function SomeClass() {
    instances.push(this);
  }

  SomeClass.prototype.getAllInstances = function(){
    return instances;
  }

  // expose the constructor
  return SomeClass;

})();