毫无疑问,在拥有大量 JavaScript 的网站上,您需要一个严格的组织模式来确保一切都有意义并且代码易于理解。 我过去曾提到我喜欢将事物分组到单独的文件中,每个文件包含一个功能特定的对象文字。 进一步,我们可以严格遵守这种模式,并确保我们将所有部分放在一个地方,所有“init”函数放在一个地方,所有事件绑定放在一个地方,并将其余部分作为一些命名良好的迷你函数,执行非常特定的操作。
不过我想知道,这是否过于组织了?
我毫不怀疑,在一个大型的 JavaScript 密集型网站上,这种严格的组织方式会带来巨大的益处。 但是对于较小的网站来说,可能就不那么明显了。
最近,我需要修复 CSS-Tricks 上的一些 JavaScript 代码。 由于我一直在使用对象文字模式编写大量 JavaScript,因此我认为将其转换为我的常规格式。 CSS-Tricks 不是一个 JavaScript 密集型网站。 大部分 JS 代码都在一个 global.js 文件中。 我保存了 JavaScript 的“之前”和“之后”状态,这样我们就可以查看并讨论一下。
“之前”
依赖项在顶部。 它们被压缩/合并到最终的 global.js 文件中。 脚本的其余部分被包装在一个 IIFE 中,以便变量不会成为全局变量。 您可以像阅读从上到下的内容一样阅读它,因为它只是一部分接一部分的功能。 所有处理该功能的代码都集中在这些代码块中,仅由空格和简短的注释隔开。
// @codekit-prepend "jquery.fitvids.js"
// @codekit-prepend "placeholder.js"
// @codekit-append "prism.js"
// Protect global namespace
(function($) {
// Make videos fluid width
$("article, .photo-grid, .single-video-wrapper, .gallery-grid .grid-5-6").fitVids({
customSelector: "video"
});
// IE 9 placeholders
$('input, textarea').placeholder();
// Search stuff
var openSearch = $(".open-search, #x-search, #refine-search");
var body = $("body");
function toggleSearch() {
if (body.hasClass("show-nav")) {
$("body").toggleClass("show-nav");
} else {
$(".search").toggleClass("open");
$(".open-search").toggle();
$(".close-search").toggle();
}
}
openSearch.on("click", function(e) {
e.preventDefault();
toggleSearch();
setTimeout(function(){
$(".search-field").focus();
}, 100);
});
var searchParts = $(".search-parts > a:not(.x-search)");
var searchForm = $("#search-form");
searchParts.on("click", function() {
var el = $(this);
var newActionURL = el.data("url");
searchForm.attr("action", newActionURL);
searchParts.removeClass("active");
el.addClass("active");
});
// Small screen navigation stuff
$("#show-hide-navigation").on("click", function(e) {
e.preventDefault();
$("body").toggleClass("show-nav");
});
// Code highlighting stuff
$("pre.lang-html, pre[rel=HTML]").find("code").addClass("language-markup");
$("code.html, code.lang-html").removeClass().addClass("language-markup").parent().attr("rel", "HTML");
$("code.javascript").removeClass().addClass("language-javascript").attr("rel", "JavaScript");
$("pre[rel=JavaScript], pre.lang-js, pre[rel=jQuery], pre.JavaScript").attr("rel", "JavaScript").find("code").removeClass().addClass("language-javascript");
$("pre[rel='CSS'], pre[rel='LESS'], pre[rel='Sass'], pre[rel='SASS'], pre[rel='SCSS']").find("code").removeClass().addClass("language-css");
$("code.css, code.lang-css").removeClass().addClass("language-css").parent().attr("rel", "CSS");
$("pre[rel=PHP]").attr("rel", "PHP").find("code").removeClass().addClass("language-javascript");
$("code.php").removeClass().addClass("language-javascript").parent().attr("rel", "PHP");
// Comments Stuff
$(".comment.buried").on("click", function() {
$(this).removeClass("buried");
});
$("#view-comments-button").on("click", function(e) {
e.preventDefault();
$("#comments").show();
$(this).hide();
});
// Illustrator links
var timer;
var illustratorLink = $(".illustrator-link").hide();
$(".deals-header, .almanac-title, .videos-title, .snippets-title, .demos-title, .gallery-header, .forums-title")
.mouseenter(function() {
timer = setTimeout(function() {
illustratorLink.slideDown(200);
}, 3000);
})
.mouseleave(function() {
clearTimeout(timer);
});
})(jQuery);
“之后”
依赖项仍然在顶部,只是我将整个代码突出显示块移动到其开放的单独文件中,因为它不需要绑定并且没有创建任何变量。
所有选择器都在顶部,分组在一起。 所有 init 函数都在一起。 所有事件绑定都在一起。 所有“操作”都是命名良好的迷你函数。
// @codekit-prepend "jquery.fitvids.js"
// @codekit-prepend "placeholder.js"
// @codekit-prepend "highlighting-fixes.js"
// @codekit-append "prism.js"
var csstricks = {
el: {
body: $("body"),
allInputs: $('input, textarea'),
searchForm: $("#search-form"),
searchOpeners: $(".open-search, #x-search, #refine-search"),
searchSections: $(".search-parts > a:not(.x-search)"),
searchField: $(".search-field"),
search: $(".search"),
openSearch: $(".open-search"),
closeSearch: $(".close-search"),
videoWrappers: $("article, .photo-grid, .single-video-wrapper, .gallery-grid .grid-5-6"),
navToggle: $("#show-hide-navigation"),
illustratorLink: $(".illustrator-link"),
headerAreas: $(".deals-header, .almanac-title, .videos-title, .snippets-title, .demos-title, .gallery-header, .forums-title"),
buriedComments: $(".comment.buried"),
viewCommentsButton: $("#view-comments-button"),
commentsArea: $("#comments")
},
timer: 0,
init: function() {
csstricks.bindUIActions();
csstricks.makeVideosFluidWidth();
csstricks.polyfillPlaceholders();
csstricks.el.illustratorLink.hide();
},
bindUIActions: function() {
csstricks.el.searchOpeners.on("click", csstricks.handleSearchClick);
csstricks.el.searchSections.on("click", csstricks.handleSearchPartsClick);
csstricks.el.navToggle.on("click", csstricks.mobileNavToggle);
csstricks.el.headerAreas.on("mouseenter", csstricks.openIllustratorLinkArea);
csstricks.el.headerAreas.on("mouseleave", csstricks.closeIllustratorLinkArea);
csstricks.el.buriedComments.on("click", csstricks.revealComment);
csstricks.el.viewCommentsButton.on("click", csstricks.revealCommentsArea);
},
makeVideosFluidWidth: function() {
csstricks.el.videoWrappers.fitVids({
customSelector: "video"
});
},
polyfillPlaceholders: function() {
csstricks.el.allInputs.placeholder();
},
handleSearchClick: function(event) {
event.preventDefault();
csstricks.toggleSearch();
setTimeout(function() {
csstricks.focusSearchField();
}, 100);
},
mobileNavToggle: function(event) {
event.preventDefault();
csstricks.el.body.toggleClass("show-nav");
},
focusSearchField: function() {
csstricks.el.searchField.focus();
},
handleSearchPartsClick: function(event) {
var el = $(event.target);
var newActionURL = el.data("url");
csstricks.el.searchForm.attr("action", newActionURL);
csstricks.el.searchSections.removeClass("active");
el.addClass("active");
},
toggleSearch: function() {
if (csstricks.el.body.hasClass("show-nav")) {
csstricks.el.body.toggleClass("show-nav");
} else {
csstricks.el.search.toggleClass("open");
csstricks.el.openSearch.toggle();
csstricks.el.closeSearch.toggle();
}
},
openIllustratorLinkArea: function() {
csstricks.timer = setTimeout(function() {
csstricks.el.illustratorLink.slideDown(200);
}, 3000);
},
closeIllustratorLinkArea: function() {
clearTimeout(csstricks.timer);
},
revealComment: function(event) {
$(event.target).removeClass("buried");
},
revealCommentsArea: function(event) {
event.preventDefault();
csstricks.el.commentsArea.show();
csstricks.el.viewCommentsButton.hide();
}
};
csstricks.init();
它们如何比较?
关于“之前”,我喜欢从上到下阅读的方式,所有相关的代码都分组在一起。 由于每个组平均只有大约 10 行代码,因此它非常易读。 如果事情变得更加复杂,我会担心该文件变得难以管理。
关于“之后”,看到所有选择器分组在一起有点奇怪。 您不知道为什么要选择这些元素,但您可以看到页面上所有与该文件相关的元素,并且可以独立于其功能管理它们。 如果您完全不熟悉这个文件,并且试图熟悉它,阅读init和bindUIActions函数是否让您明白了这一点? 阅读这些函数的速度是否比扫描整个“之前”文件更快? 元素和函数都以“csstricks.”开头是否会造成混乱?
尽管将一大块代码移到了单独的文件中,“之后”的代码行数也增加了 25 行。
我没有所有答案。 我仍然不确定我更喜欢哪一种,或者混合方法是好是坏。
这正是为什么我对使用 Backbone.js 和 Require.js 越来越喜欢的原因。
总的来说,我认为你可能触及到了我一段时间以来一直的想法,那就是我不太喜欢使用 JavaScript 对象文字来组织应用程序代码的模式。
对我来说,代码块感觉很孤立。 您必须将所有内容都放在函数中,这些函数具有同等重要的级别(视觉上)。 我发现这种方式可读性较差,并且迫使您以这种方式编写代码,而不是根据问题的使用情况来组织函数和变量之间的关系。
几乎没问题,但是你在“之后”的代码中有一个丑陋的全局变量。 为什么你没有把它闭包起来? ;)
我更喜欢第二种方法,理由与您相同。 此外,将所有选择器都放在一个地方是一个好主意,如果您有一个“喜怒无常”的后端,它喜欢更改元素的名称(例如,如果您为第三方页面制作用户脚本)。
公平地说,你的网站在 JavaScript 方面更像是业余爱好者的水平,主要使用 jQuery。 如果你能将你的 JS 代码保留在一个(甚至几个)文件中,总代码行数不超过 500-1000 行,那么实际上并没有那么重要。 一旦超出这些限制,JS 代码组织就会变得非常重要。
你需要以不同方式组织代码的另一个原因是你的代码结构。 有真正的、独立的代码模块(无论是使用库还是自制的闭包和“类”),添加单元测试,以及需要更改哪些模块出现在哪些页面上,代码组织就变得重要得多。
对于像你现在的网站一样简单的 JS 代码? 只要你喜欢哪个就用哪个。
完全正确。 我认为这就是为什么这会是一个有趣的比较。 毫无疑问,一个真正大型的 JavaScript 应用程序需要结构,这篇文章中的“之后”示例就是一种可能的方式。 问题是这种严格的结构对于像这样的“业余爱好者”网站来说是阻碍还是帮助。
同意。 一旦复杂度超过某个点,缺乏或组织不当就会真正开始伤害你。 我希望有更多关于大型 js 应用程序的整体应用程序组织方面的资源。 我正在一起使用 Backbone、Marionette 和 Require,并且非常喜欢它们,但从结构上来说,我感觉自己有点像是在重新发明轮子(有时很糟糕)……
我不是真正的 JavaScript 程序员,更像是一个脚本小子。 我的主要语言是 ABAP,SAP 的语言。 也就是说,你提到的内容与任何语言都相关。 那就是模块化和可读性。 这是关于面向对象、过程式和函数式编码风格的经典讨论。
我同意 Bradley 的观点。 这就像我们在工作中使用的编码标准。 如果它很小很简单,就保持它的简单性。 如果它变大了,就分解成小而简单的组件,并为可读性进行组织。 我们必须为其他人维护代码。
很棒的文章,结构化 JavaScript 的好方法。 如果我要构建更大更复杂的 JavaScript,我会为每个“对象”创建自己的文件,并使用命名空间来进行良好的结构。
我也喜欢 Backbone.js,但我更喜欢使用 Marionette.js 来进一步组织代码。 即使不使用 Backbone 的 REST 同步功能,Marionette 仍然非常适合组织。 它确实将 Backbone 带到了一个新的高度。 模块非常适合将相关的代码放在一起。 它减少模板代码的程度真正取决于您使用了多少功能。
我一直听说 Backbone.js、Require.js 等等,但真正的问题不是 JavaScript 难以维护吗? 许多因素会影响从小型到中型网站的维护。 例如,如果有多个开发人员在同一个网站上工作,那么您可能需要选择完全不同的策略。
我在之前大量使用过 jQuery,每次项目变大时,我们都会最终围绕它构建某种框架。 目前正在做一些 GWT 的工作,不得不说,就组织而言,它是目前最好的。 (注意,我并不是在评论功能或开发的难易程度,只是 GWT 的结构方面 :))
我想附和上面对 Require.JS 的支持言论。
此外,就像其他语言一样,设计模式可以帮助组织代码:http://addyosmani.com/resources/essentialjsdesignpatterns/book/
更简单地说,如果函数在顺序上声明,JSLint 会报错,所以符合规范的代码往往会将依赖项放在顶部,正如您所建议的那样。
在我现在正在进行的一个 Web 应用程序中,我有一些类似于 csstricks.makeVideosFluidWidth 和 csstricks.polyfillPlaceholders 的函数,它们在 DOM 准备好时执行。
如果我使用 AJAX 加载包含视频或占位符的内容,这些函数显然不会执行,但加载的 HTML 有时需要它们。
下面是我用来解决这个问题的方法,**但我真的很想知道其他人是如何解决这个问题的。**我创建了一个用于“准备”DOM 的函数,并在 DOM 准备好时在整个文档中执行它,并且只在动态添加的部分准备好时执行它。
对我来说,这似乎是一个合理的方法。尤其是你如何限制后续调用中需要准备的内容范围。正如你所知,这理想情况下只适用于绝对需要重新运行的东西。如果只是事件绑定,事件委托应该可以帮助你(例如 .live())。
Chris,如果你指的是 jQuery.live(),它在 jQuery 1.7 中已被弃用,并在 1.9 中被移除。
https://api.jqueryjs.cn/live/
是的,是的,需要注意。但你知道我的意思,事件委托,无论你如何实现它。将事件绑定到 DOM 树中较高层的元素,并在事件冒泡时检查目标。这样你就不用在添加新内容时重新绑定了。
在阅读了“更快网站”和“高性能 JavaScript”等几本电子书后,我也开始重新排列我的脚本。有很多方法可以排列脚本,我敢肯定我们工作中几乎都尝试过所有方法,哈哈。有时很难坚持使用一种特定的方法。
我们有 3 个人使用 JavaScript 文件,所以它必须清晰且一致。我们大约有 15,000 行自己的 JavaScript 代码(其中 7,000 行只是订单表单对象的 JavaScript 代码),分布在约 50 个文件中(我们执行 make install 构建以返回 1 个同步文件和 1 个异步文件)。
首先,我们会根据脚本是否需要在页面加载时修改页面来分离脚本(例如,它是一个触摸/点击事件吗?是 - 异步加载)。这样可以减少页面加载时的阻塞时间。如果不是,则在 window load 事件中异步加载它。对于在异步脚本加载之前需要调用的任何函数,我们有一个 pluginReady 函数,它使用 jquery deferreds 在计时器上尝试初始化插件,直到插件下载,或者只是创建存根函数。
你那里的“before”脚本就像我们的“front.js”文件一样。它是一个脚本集合,只在一次修改 DOM,例如在移动浏览器上重新排列页面。在与多个开发人员合作时最好保持这种方式,因为他们通常不理解不同的模式。它很简单,易于维护。
任何我认为是插件或需要交互和一些状态维护的东西,都会有自己的文件,放在 scripts 或 libs 文件夹中(新插件从 scripts 开始,一旦稳定、可靠且完全清理代码后,就将其移到 libs 中 - 通常是对象文字模式,或者我们下载的库,例如占位符、现代化等)。
我们对所有变量和函数进行命名空间,这样我们就可以在必要时或在函数使用后从对象中删除它们。这也有帮助,因为您可以更轻松地查看已设置哪些变量,而不会在 DOM 的窗口级别感到困惑。我们使用闭包传入命名空间的参数和函数名称,以保持全局 ns 清晰并减少与其他函数的冲突。
例如
Namespace.builder ={};
(Function(context) {
// 在这里,我们有私有和公共变量以及函数
Var size =14;
Context.price = 12;
})(namespace.builder);
我们没有最好的 JavaScript 解决方案,但它运行得很好。加载速度快,易于维护和阅读。
在你的“after”中,你实际上不能拥有“private”变量,理想的解决方案应该是
但是,正确的模式取决于你想实现什么。没有正确解决方案。
似曾相识 :)
(点击了解更多关于这种模式的信息)
我认为你可以“过度组织”你的 JavaScript 代码。虽然“After”乍一看似乎更有条理,但它也有一些明显的缺点。没有代码注释、不必要的全局变量声明以及一些为了组织而组织的额外内容。例如,“polyfillPlaceholders”函数真的在为代码增加组织性,还是仅仅增加了臃肿?为什么不把它像“Before”示例那样放在 init 函数中,并添加一个不错的代码注释呢?我可以进入完整的代码审查模式,但我认为这不是问题的重点。:)
根据我的经验,网站以意想不到的方式发展和变化。一个“业余爱好者”到“专业人士”的网站(或者类似的东西)可能会在一夜之间发生。这就是为什么你希望你的代码组织良好。我认为“After”方法很好,并且肯定会是更好的方法,但我不害怕不断地根据你的需要对其进行调整。
我的观点
http://xkcd.com/974/
;)
我实际上将 JS 代码视为面向对象的代码。我们有一些名为“project”的文件夹,在每个项目中,都有一些位于文件夹中的文件,每个文件都可以被其他文件依赖。这使我们能够将文件分离到“类”或功能区域的较小单元中,并在单独的文件中管理它们。这种基础架构使我能够为网站中的所有基本操作创建“base”文件,并为唯一的页面创建“dedicated”文件,这样我就可以减少网站中常用页面使用的资源量,并使其更快。专用文件在“base 文件”之后加载到特定页面中。
优点
缺点
顺便说一下 - 我们对 CSS 文件使用相同的基础架构。除了上面提到的之外,这种基础架构还允许我们针对我们想要支持的每种语言对 CSS 代码进行调整(处理 RTL 调整、根据语言自动替换图像、从单个图像生成雪碧文件)。但这又是另一个问题。
对于相当小的 JS 文件,我通常会做一些类似于你第一个示例的事情。不过,我喜欢将代码分组到较小的函数中,并从 init() 函数中调用它们。我认为这使得代码更易于阅读,此外,你只需转到 init() 函数就可以看到哪些函数被调用以及它们的调用顺序。有点像旧的 C 程序。
对于一个非常依赖 JS 的网站或 Web 应用程序,在构建代码时我会采用不同的方法。
我不确定我对像你在“after”中那样将选择器和绑定分组的看法。删除一个函数可能会很麻烦,因为你必须查看多个地方以确保删除与它相关的所有内容,这样你就不会最终得到一堆不再使用的选择器,例如。或者尝试将事件处理程序添加到不再存在的元素中。
在“before”版本中,你将所有内容都包装在一个匿名函数中以保护全局命名空间。在你的“after”版本中,你将所有内容都移到了一个全局对象中。你是否考虑过混合方法,即仍然保护全局命名空间,但在匿名函数中有一个名为“namespace”的单一对象来保存所有代码?例如
在你几乎一半的函数中,你的对象文字是当前上下文中的对象,所以你不需要执行
csstricks.someMethod();
相反,你可以使用“this”。例如
在你的事件处理程序中,HTMLElement 是当前上下文中的对象,所以你仍然希望它们在内部使用“csstricks.someMethod();”。
在发现自己多次访问对象属性的方法中,你应该为它创建一个简短的别名。例如,这里你一直在访问 csstricks.el
由于 csstricks 已经是当前作用域中的对象,所以你可以将它们全部更改为 this.el。更重要的是,创建一个简短的别名,如下所示
请注意,我还将 on 方法的第二个参数中的“csstricks.”替换为了“this.”。
你也可以重新组织代码,以便所有事件监听器都分组在一起。你有一些方法,比如 focusSearchField 和 toggleSearch,它们被事件监听器调用,但实际上不是事件监听器方法本身,所以我建议稍微调整一下它们。
对于这么多的代码,实际上无论是哪种方式,收益都很小,所以可能主要取决于你个人觉得哪种方法更容易使用。总体而言,你的函数很小巧,这使得维护和理解它们变得容易。不过,可以添加一些注释。
旁注
在你的 openIllustratorLinkArea 中,你应该在调用 setTimeout 之前添加 clearTimeout(csstricks.timer);。你的代码中存在一个(非常小的)竞争条件的可能性,不过唯一的副作用是 slideDown 会出现卡顿现象。
需要记住一些事情。当你说“将所有选择器分组”时,你实际上做的不仅仅是将选择器分组,而是将所有元素分组。也就是说,你实际上正在获取它们并存储对这些元素的引用。从外观上看,你无法清楚地知道它们在哪里或何时被使用,因此你还有可能删除使用一个或多个这些元素的代码,但却忘记删除获取该元素的代码(这样你就会毫无理由地获取它们)。不过,鉴于这个文件的大小,我认为这不会造成太大问题。
以下是我重新组织代码的方式
这取决于具体情况。 重要的是要意识到它位于全局命名空间中。 通常我喜欢把它设置为全局,以便其他代码可以使用它。
是的,这是一个很好的观点。 我一开始总是使用
this来引用它,但过了一段时间后,我发现它很令人困惑。 如您所知,this在 JavaScript 中会根据上下文发生很大变化,我发现专门引用对象更清晰。 不过完全只是个人喜好。感谢您对此的所有想法和工作,非常感谢!
当然,拥有一个全局“命名空间”对象会很有帮助。 在您的情况下,我感觉您将所有代码都保留在这个文件中,在这种情况下可能不需要全局命名空间。 我同意,这取决于具体情况。
不用谢。:)
我最近在很多大小项目中都使用了对象字面量表示法,并不觉得它太过分。 我认为这在很大程度上取决于个人偏好,至于要应用哪种模式,但我肯定认为有总比没有好。 我来自面向对象的背景,所以我更喜欢看到带有命名空间和隔离级别的 JavaScript。 当然,并非所有开发人员都这样。 我们都有自己的偏好和对特定模式或方法的亲和力。
对于那些不同意的人,您如何能忽视对 JavaScript 代码进行更清晰、更合乎逻辑的布局? 对我来说,它似乎总是双赢,因为它不仅能促进更好的编码习惯,还能减少错误,并让性能改进更容易发现。
就而言,JavaScript 只是一个页面或更少; 我注意到 JavaScript 往往会增长,从一开始就使用某种模式或思想流派会阻止它有一天变成一个巨大的、无法管理的、单片式野兽,没有人愿意去驯服它。
是否更难阅读? 对于新手和任何不熟悉组织或不习惯阅读其他人代码的人来说,是的。
但请不要为了新手和没有经验的人而工作。 他们在尝试——或抱怨无法——接触真实软件之前,应该去学习并努力提升自己。
当您处于教学环境中时,您应该担心读者是否能轻松理解。 他甚至可能付钱给您从您的代码中学习。 但在生产/现实世界开发环境中,重要的是可维护性和可靠性(包括稳定性和性能)。
如果您对代码进行了很好的组织,并且需要有人来维护它,而他无法理解您的良好组织,请不要为了新手能阅读而改变它。 最好是教这些新手了解您的组织方式。
具体来说,对于 JavaScript,如果我考虑让代码易于阅读,并查看它应用的位置以及哪些部分适用于每个元素,我永远不会使用 .js 文件来放置我的代码。 我会直接将所有函数放在 HTML 文档中,并使用 onMouseOver、onClick 以及所有这些 XHTML 1.0 Strict 无效属性将这些函数附加到每个 HTML 元素。 一旦我删除这些属性并使用 jQuery 选择器进行绑定,就会变得非常难弄清楚到底是什么控制了元素的行为。
我对 JavaScript 还比较陌生,但我建议将所有内容分成逻辑块,就像这样: http://snippi.com/s/r5kl9vo
我不确定代码是否真的有效,因为我无法真正尝试它。 我用 JSHint 运行过它,但没有报错。
旁注:我是一个空格控,所以代码中有很多行。
非常棒!
我喜欢将配置分成相关的部分。
是的,我真的很喜欢有一个明确的配置部分。 我还发现,当所有内容都有一个清晰的结构时,更容易找到我想要的东西。 它也使添加和删除内容变得非常容易。
明显的缺点是,就像我之前提到的那样,代码会变得更长。 不过,当您为了生产而进行缩小处理时,这可能不是什么大问题。:)
我编写了很多大型 JavaScript 应用程序,我不得不说我更喜欢您的“之前”,原因是:在那里,您通过“功能模块”而不是“事物类型”来组织所有内容。 根据我的经验,前者比后者扩展得更好,而且对于小型项目来说也更清晰,所以无论哪种方式,您都能获益。
“功能区域”比“事物类型”更抽象,因此它更能体现程序员的意图,因此对未来的更改更有弹性,而且更易读。
作为旁注,程序组织中“功能”与“类型”的二分法类似于管理中“产品组”与“学科组”的二分法。 您的里程数可能会有所不同,但我认为从一个专注于单一产品的跨学科团队那里获得更好的结果。
绝对的,对我来说上面的“之前”要好得多。
有趣的帖子,质疑 JavaScript 代码的重构。
http://www.frankysnotes.com/2013/04/reading-notes-90.html
我也真的很喜欢第二个,因为它扩展得更好,但就像艾伦建议的那样,最好将它分成块。
我一直都在做的是创建一个全局对象,然后将其分成适当的块。 例如(忍受语法错误,没有测试)
if (typeof(csstricks) == "undefined") { var csstricks = {}; } // create global object if it doesnt exist. csstricks.searchStuff = { init: function(){ //call searchStuff init functions here }, toggleSearch: function(){} } csstricks.responsiveStuff = {}; csstricks.socialStuff = {};等等。 我想这融合了伊恩和艾伦上面建议的内容。 它只有单个全局变量的好处,可以访问其他应用程序/代码/等等。
@赛义夫,同意……这就是我组织大多数项目的方式,无论大小。 通常每个模块都在自己的文件中,但如果足够小,它们都可以放在同一个文件中。 对您的代码只需进行一些调整
虽然您的第二个版本更井然有序,但最好将某些 DOM 搜索调用的执行延迟到 jQuery 中,并根据需要进行延迟加载。
“之后”的示例是我过去 4 年来编写 JavaScript 的方式。 我现在仍然看到很多意大利面条式的 JavaScript 代码,但我认为随着所有新的 MVC 框架的出现,井然有序的代码变得越来越普遍。
几年前,我写了这篇关于如何使用原型作为构建井然有序的 JavaScript 库的基础的小文章
http://www.pdvictor.com/en/?sv=&category=just%20code&title=Create%20Object%20Oriented%20Javascript
我个人将应用程序分成具有一个明确目标的小模块。 我通常将这些模块实现为类的实例。 我将每个类都放在自己的文件中,这样我的 JavaScript 文件就很小,易于管理。
我将“公共”访问器放在最上面,将“私有”方法放在每个类的最下面。
在您的示例中,我可能将“任务”分成单独的文件,然后将它们连接在一起。 但我会在单独的文件中处理它们。
感谢作者和所有评论者。 对于像我这样的 JavaScript 新手来说,这是一篇非常有用的帖子!
约翰。
很棒的帖子。 当然,我很想有一个小按钮来分享到 Twitter,:暗示,暗示
好吧,我将通过 @DRicardoDesigns 自己来做……。
将这个书签工具栏放在您的书签栏中,您将永远不再遇到这个问题: https://dev.twitter.com/docs/share-bookmarklet
您好
这两种方法都应该混合使用,但一定要从方法 1 开始(而不是反过来)。
当您的代码超过 2000 行时,方法 2 就会变得非常难以维护。 向上滚动以确定哪个选择器,向下滚动,附加您的功能……而方法 1 只需直接编码即可。
通常我会发现,只要有某种结构思考,它就能保持可维护性。 不要过度混合使用,因为这样代码就会变得很乱。 也不要从方法 2 开始,然后返回到方法 1。 一旦您走上了方法 2 的道路,您基本上就应该坚持下去。 而使用方法 1,您可以让代码的某些“更大”部分使用方法 2,而不会与代码的其余部分“不同”。
干杯
PS:不错的网站!