JavaScript组织 | 模块模式与模块

5
我正在将我的代码组织成20-60行的模块,通常采用模块模式。我想要一个结构良好的面向对象的JavaScript库。
这是最佳方式吗?代码已经经过测试并且可运行。
我喜欢它,因为程序员可以从库中提取模块,并根据需要使用它们,它们是自包含的。
这里有工具、消息、效果和文本,都包含在NS中。
问题是什么?
这是组织我的库的好方法(最佳实践)吗?
注意:
到目前为止,在评论和答案中还没有任何共识...非常沮丧。
外层模块模式
var NS = ( function ( window, undefined ) 
{ 
/* All Modules below here */ 
} )( window );

工具

/**
 *Tools
 *    getTimeLapse - benchmark for adding
 */

var Tool = ( function () 
{
    var Tool = function ( ) 
    {
    };
    Tool.prototype.getTimeLapse = function( numberOfAdds ) 
    {
        var end_time;
        var start_time = new Date().getTime();
        var index = 0;           
        while ( index <= numberOfAdds )
        {
            index++;
        }
        end_time = new Date().getTime();
        return ( end_time - start_time );
    };
    return Tool;
} () );

信息

/**
 *Message
 *    element - holds the element to send the message to via .innerHTML
 *    type - determines the message to send
 */

var Message = ( function () 
{
    var messages = 
    {
        name:         'Please enter a valid name',
        email:        'Please enter a valid email',
        email_s:      'Please enter a valid email.',
        pass:         'Please enter passoword, 6-40 characters',
        url:          'Please enter a valid url',
        title:        'Please enter a valid title',
        tweet:        'Please enter a valid tweet',
        empty:        'Please complete all fields',
        same:         'Please make emails equal',
        taken:        'Sorry, that email is taken',
        validate:     'Please contact <a class="d" href="mailto:foo@foo.com">support</a> to reset your password',
    };
    var Message = function (element) 
    {
        this.element = element;
    };
    Message.prototype.display = function( type ) 
    {
        this.element.innerHTML = messages[ type ];
    };
    return Message;
} () );

作用

/**
 *Effects
 *    element - holds the element to fade
 *    direction - determines which way to fade the element
 *    max_time - length of the fade
 */

var Effects = ( function () 
{
    var Effects = function ( element )
    {
        this.element = element;
    };
    Effects.prototype.fade = function( direction, max_time ) 
    {
        var element = this.element;
        element.elapsed = 0;
        clearTimeout( element.timeout_id );
        function next()
        {
            element.elapsed += 10;
            if ( direction === 'up' )
            {
                element.style.opacity = element.elapsed / max_time;
            }
            else if ( direction === 'down' )
            {
                element.style.opacity = ( max_time - element.elapsed ) / max_time;
            }
            if ( element.elapsed <= max_time ) 
            {
                element.timeout_id = setTimeout( next, 10 );
            }
        }
        next();
    };
    return Effects;
} () );

文本

/**
 *Text
 *    form_elment - holds text to check
 */

var Text = ( function () 
{
    var Text = function ( form_element )
    {
        this.text_array = form_element.elements;
    };
    Text.prototype.patterns = 
    {
        prefix_url:     /^(http:)|(https:)\/\//,
        aml:            /<(.+)_([a-z]){1}>$/,
        url:            /^.{1,2048}$/,
        tweet:          /^.{1,40}$/, 
        title:          /^.{1,32}$/,
        name:           /^.{1,64}$/, 
        email:          /^.{1,64}@.{1,255}$/,
        pass:           /^.{6,20}$/
    };
    Text.prototype.checkPattern = function( type ) 
    {
        return this.patterns[ type ].exec( this.text_array[type].value );
    };
    Text.prototype.checkUrl = function( type ) 
    {
        return this.patterns[ type ].exec( this.text_array.url.value );
    };
    Text.prototype.checkSameEmail = function() 
    {
        return ( ( this.text_array.email.value ) === ( this.text_array.email1.value ) );
    };
    Text.prototype.checkEmpty = function() 
    {
        for ( var index = 0; index < this.text_array.length; ++index ) 
        {
            if ( this.text_array[ index ].value === '') 
            { 
                return 0; 
            }
        }
        return 1;
    };
    return Text;
} () );

@gillesc 所有全局变量都不好,即使只有一个命名空间。 - Raynos
@jbabey,这不是我想说的。你不应该有全局变量,没有命名空间,你的代码也不应该在一个单独的文件中。 - Raynos
1
@Raynos,我不想打破你的幻想,但是没有至少一个全局变量是不可能的 :) - jbabey
2
@Raynos 即使您使用 AMD 或 commonJS 等模块加载系统,这些库仍然会暴露全局变量。没有一种访问 JavaScript 的方法是不可能执行 JavaScript 的。 - jbabey
2
以 require.js 为例,requiredefine 都是全局变量。我很难理解你为什么不明白这个概念。 - jbabey
显示剩余9条评论
3个回答

2

我建议你只需一次性设置原型属性,以使代码更加清晰并减小其体积。因此,不需要再像下面这样做:

Object.prototype.method1 = function(){};
Object.prototype.method2 = function(){};

你需要做的是

Object.prototype = {
    method1: function(){},
    method2: function(){}
};

如果您需要保留构造函数引用,则应在之后重新分配构造函数,这是推荐的做法。有关更多详细信息,请参见此答案


2
  1. 永远不要重新分配 Object.prototype
  2. 重新分配原型时,务必将 constructor 属性放回去。
- Raynos
@Raynos,你能再解释一下吗? - Daniel Ribeiro
@DanielRibeiro 1)很明显,2)https://dev59.com/rm445IYBdhLWcg3wR4RN - Raynos
@The All Foo 我不这么认为。 - Daniel Ribeiro

1

我个人更喜欢使用像ncore这样的模块化代码组织库

这鼓励你将代码编写为一组模块(每个文件一个模块),然后使用依赖注入和引导将它们连接在一起。

代码稍微具有可移植性,因为模块本身就是对象,但是如果不使用 ncore,则会失去优势。

排行榜应用程序展示了面向对象代码组织的详细示例


1
一些建议... 首先是创建一个命名空间对象作为您的库的作用域... jQuery 使用 "jQuery" 和 "$",underscore 使用 "_". 我倾向于使用 "CompanyName.SiteName"。
if (typeof CompanyName == "undefined") var CompanyName = {};
CompanyName.SiteName = CompanyName.SiteName || {};

第一行明确检查了undefined,否则在使用SiteName属性方法时,根变量会在许多浏览器中出现错误。

从那里开始,我会进行一些调整... 当您在内联调用匿名函数时,最好将整个调用包装在括号中。

CompanyName.SiteName.ModuleName = (function(w){
    ...
    return moduleImplementation;
}(window || this)); //CompanyName.SiteName.ModuleName

这样做可以避免混淆,因为括号将整个内容包含在内,并在模块声明的末尾添加注释。

根据上面的注释,您可能希望将原型声明作为更单一的语句。我建议不要这样做,因为较长的模块可能会影响可读性。

myModule.prototype = {
    "method1": function(){
    }
    ...
    "methodN": function(){
       //by the time you get here, you may not see the top, and where you are nested in
    }
};

//with the dot-notation, or hash notation
myModule.prototype.methodN = ...
myModule.prototype["methodN"] = ...
//you can see where you are binding to at that function

你可能还想了解RequireJSAMD

还有一种处理简单对象并使用函数绑定器的概念。将库视为一组函数(类似于C导出函数),这些函数传递并处理简单的对象/类型。这真的取决于您的需求/用法以及您的具体需求和用法。

您还可以查看像KnockoutJSUnderscoreBackbone这样的JavaScript库,以获取一些示例。


命名空间不好。请勿使用它们。 - Raynos
@Raynos 命名空间很好,它们对于代码隔离非常有用。不好的是当你进行多个深度嵌套的命名空间调用时...通常最好为你的命名空间/函数创建本地变量别名。但这是模块开发的另一个问题。除非你有一个促进封装的环境(比如Node)。 - Tracker1
@TheAllFoo 我没有看到Arc作为一个命名空间,我看到的是Arc、Tools和Message。 - Tracker1

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