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

Can I modularize my javascript code like this?

本文关键字:代码 像这样 javascript 我的 模块化 我可以      更新时间:2023-09-26

我真的不喜欢我所有的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));
})();

我可以这样做吗?有没有什么我忽略了的东西,从长远来看可能会导致问题?这样做有可能污染全局范围吗?

您所描述的是立即调用的函数表达式模块模式。

要导出函数和对象,您需要将它们附加到全局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可以保存状态,但它们是有效的单例,尽量确保创建对象并将任何状态放入其中以避免全局状态。

这样做是否有可能污染全局作用域?

是的这是可能的;你仍然应该小心命名你的命名空间——比如不要使用window.jQuery !

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

要明确你正在创建window对象的属性。把

_randomGenerator = new RandomNumberGenerator();

window._randomGenerator = new RandomNumberGenerator();

这个显式声明在严格模式下是强制性的。

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

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

window.module = {};

那么剩下的模块,就可以将自己注册为模块对象的属性,而不必扭曲它们的名称。

module.randomNumberGenerator = new RandomNumberGenerator();
function RandomNumberGenerator() {
  ...
}    

那么在任何一个文件中你都可以使用这个实例。

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

这是可行的,但它过于复杂,除非您声明了一些数据,需要将其放入作用域以保持隔离。

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

// 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;
})();