随着像jQuery这样的JavaScript框架使客户端web应用程序更丰富,功能更强大,我开始注意到一个问题…

你到底是怎么组织起来的?

把所有的处理程序放在一个地方,并为所有事件编写函数? 创建函数/类来包装您的所有功能? 疯狂地写作,只希望结果是最好的? 放弃,找一份新工作?

我提到了jQuery,但它实际上是一般的JavaScript代码。我发现,当一行一行开始堆积时,管理脚本文件或找到您要找的内容变得越来越困难。我发现的最大问题可能是,做同一件事有太多的方法,很难知道哪一种是目前普遍接受的最佳实践。

有什么通用的建议可以让你的.js文件像你的应用程序的其他部分一样漂亮整洁吗?或者这只是IDE的问题?还有更好的选择吗?


EDIT

这个问题主要是关于代码组织,而不是文件组织。有一些合并文件或拆分内容的好例子。

我的问题是:目前普遍接受的组织实际代码的最佳实践方式是什么?您的方法是什么,甚至推荐的方法是什么,以与页面元素交互并创建互不冲突的可重用代码?

有些人列出了名称空间,这是个好主意。还有什么其他方法,更具体地说,处理页面上的元素并保持代码的组织和整洁?


当前回答

你可以使用jquery mx(在javascriptMVC中使用),这是一组允许你使用模型、视图和控制器的脚本。我曾在一个项目中使用它,并帮助我创建结构化javascript,由于压缩,脚本大小最小。以控制器为例:

$.Controller.extend('Todos',{
  ".todo mouseover" : function( el, ev ) {
   el.css("backgroundColor","red")
  },
  ".todo mouseout" : function( el, ev ) {
   el.css("backgroundColor","")
  },
  ".create click" : function() {
   this.find("ol").append("<li class='todo'>New Todo</li>"); 
  }
})

new Todos($('#todos'));

如果对视图和模型部分不感兴趣,也可以只使用querymx的控制器端。

其他回答

代码组织要求采用约定和文档标准: 1. 物理文件的命名空间代码;

Exc = {};

2. 在这些命名空间javascript中分组类; 3.设置原型或相关的函数或类来表示真实世界的对象;

Exc = {};
Exc.ui = {};
Exc.ui.maskedInput = function (mask) {
    this.mask = mask;
    ...
};
Exc.ui.domTips = function (dom, tips) {
    this.dom = gift;
    this.tips = tips;
    ...
};

4. 设置约定以改进代码。例如,将其所有内部函数或方法分组在对象类型的class属性中。

Exc.ui.domTips = function (dom, tips) {
    this.dom = gift;
    this.tips = tips;
    this.internal = {
        widthEstimates: function (tips) {
            ...
        }
        formatTips: function () {
            ...
        }
    };
    ...
};

5. 制作名称空间、类、方法和变量的文档。必要时也讨论一些代码(一些fi和for,它们通常实现代码的重要逻辑)。

/**
  * Namespace <i> Example </i> created to group other namespaces of the "Example".  
  */
Exc = {};
/**
  * Namespace <i> ui </i> created with the aim of grouping namespaces user interface.
  */
Exc.ui = {};

/**
  * Class <i> maskdInput </i> used to add an input HTML formatting capabilities and validation of data and information.
  * @ Param {String} mask - mask validation of input data.
  */
Exc.ui.maskedInput = function (mask) {
    this.mask = mask;
    ...
};

/**
  * Class <i> domTips </i> used to add an HTML element the ability to present tips and information about its function or rule input etc..
  * @ Param {String} id - id of the HTML element.
  * @ Param {String} tips - tips on the element that will appear when the mouse is over the element whose identifier is id <i> </i>.
  */
  Exc.ui.domTips = function (id, tips) {
    this.domID = id;
    this.tips = tips;
    ...
};

这些只是一些技巧,但这对组织代码有很大帮助。记住,你必须自律才能成功!

您可以将脚本分解为单独的文件进行开发,然后创建一个“发布”版本,在这个版本中,您可以将它们全部塞在一起,并在上面运行YUI Compressor或类似的程序。

几天前,37Signals的人发布了一个RTE控件。他们制作了一个库,使用一种预处理器命令捆绑javascript文件。

我一直在用它来分离我的JS文件,然后在最后合并为一个。这样我就可以分离关注点,并且最终只有一个文件通过管道(gzip的)。

在模板中,检查是否处于开发模式,并包含单独的文件,如果处于生产模式,则包括最后一个文件(您必须自己“构建”)。

您没有提到服务器端语言是什么。或者,更确切地说,您在服务器端使用什么框架(如果有的话)。

IME,我在服务器端组织东西,让它全部震动到网页上。该框架的任务不仅是组织每个页面必须加载的JS,还包括与生成的标记一起工作的JS片段。这样的片段通常不希望发出超过一次——这就是为什么它们被抽象到框架中,以便代码处理该问题。: -)

对于必须发出自己的JS的最终页面,我通常发现生成的标记中有一个逻辑结构。这种本地化的JS通常可以在这种结构的开始和/或结束处组装。

请注意,这些都不能免除您编写高效JavaScript的责任!: -)

受之前帖子的启发,我制作了一个Rakefile和供应商目录的副本,并与WysiHat (changelog提到的RTE)一起分发,并进行了一些修改,包括使用JSLint进行代码检查和使用YUI Compressor进行缩小。

这个想法是使用Sprockets(来自WysiHat)将多个JavaScripts合并到一个文件中,在发布之前用JSLint检查合并文件的语法,并用YUI Compressor缩小它。

先决条件

Java运行时 红宝石和耙子宝石 您应该知道如何将JAR放入Classpath

现在做的

下载Rhino并将JAR(“js.jar”)放到类路径中 下载YUI Compressor并将JAR (build/yuicompressor-xyz.jar)放到类路径中 下载WysiHat并将“vendor”目录复制到你的JavaScript项目的根目录 下载JSLint for Rhino,并将其放到“vendor”目录中

现在,在JavaScript项目的根目录中创建一个名为“Rakefile”的文件,并向其添加以下内容:

require 'rake'

ROOT            = File.expand_path(File.dirname(__FILE__))
OUTPUT_MERGED   = "final.js"
OUTPUT_MINIFIED = "final.min.js"

task :default => :check

desc "Merges the JavaScript sources."
task :merge do
  require File.join(ROOT, "vendor", "sprockets")

  environment  = Sprockets::Environment.new(".")
  preprocessor = Sprockets::Preprocessor.new(environment)

  %w(main.js).each do |filename|
    pathname = environment.find(filename)
    preprocessor.require(pathname.source_file)
  end

  output = preprocessor.output_file
  File.open(File.join(ROOT, OUTPUT_MERGED), 'w') { |f| f.write(output) }
end

desc "Check the JavaScript source with JSLint."
task :check => [:merge] do
  jslint_path = File.join(ROOT, "vendor", "jslint.js")

  sh 'java', 'org.mozilla.javascript.tools.shell.Main',
    jslint_path, OUTPUT_MERGED
end

desc "Minifies the JavaScript source."
task :minify => [:merge] do
  sh 'java', 'com.yahoo.platform.yui.compressor.Bootstrap', '-v',
    OUTPUT_MERGED, '-o', OUTPUT_MINIFIED
end

如果你正确地完成了所有操作,你应该能够在控制台中使用以下命令:

rake merge——将不同的JavaScript文件合并为一个 Rake检查——检查代码的语法(这是默认任务,因此只需输入Rake) rake minify——准备JS代码的缩小版本

关于源合并

使用Sprockets, JavaScript预处理器可以包括(或要求)其他JavaScript文件。使用以下语法来包含初始文件(名为“main.js”,但你可以在Rakefile中更改)中的其他脚本:

(function() {
//= require "subdir/jsfile.js"
//= require "anotherfile.js"

    // some code that depends on included files
    // note that all included files can be in the same private scope
})();

然后……

看看WysiHat提供的Rakefile来设置自动化单元测试。不错的东西:)

现在揭晓答案

这并没有很好地回答最初的问题。我知道,对此我很抱歉,但我把它贴在这里,因为我希望它能对其他人整理他们的混乱有所帮助。

我解决这个问题的方法是尽可能多地进行面向对象建模,并将实现分离到不同的文件中。那么处理程序应该尽可能短。List singleton的例子也是一个很好的例子。

和名称空间……它们可以被更深层次的物体结构所模仿。

if (typeof org === 'undefined') {
    var org = {};
}

if (!org.hasOwnProperty('example')) {
    org.example = {};
}

org.example.AnotherObject = function () {
    // constructor body
};

我不太喜欢模仿,但是如果您有很多想要移出全局作用域的对象,这可能会很有帮助。