【译】Using Objects to Organize Your Code
耗了一个晚上吐血翻译不过也学到了不少...《使用对象来组织你的代码》,翻译中发现原作者在原文中有部分代码有误或不全,本文已修改和添加~
丽贝卡·墨菲原文链接:http://rmurphey.com/blog/2009/10/15/using-objects-to-organize-your-code
当你不只是使用jQuery的简单片段而是开始开发更复杂的用户交互,你的代码会变得笨重和难以调试,这篇文章通过使用对象字面量的形式向你展示如何在行为特征的角度思考这些交互。
在过去几年,JavaScript库让初级开发者有能力为他们的站点制作炫酷的交互,就像jQuery,有着非常简单的语法得以让零编程经验的人装饰他们的网页,一个插件或自定义的几十行代码运行出的效果就能给人留下深刻印象。
但是等等,现今的需求早已改变了,现在你的代码可能需要根据ID的不同而被重用,这样的话用jQuery(或其他库)的编写的代码片段看似用处不大了,它们只是代码片段不是吗?当你不使用插件而实现 show() 或 hide() 的功能应该怎么设计你的代码呢?
Introducing the Object Literal Pattern 对象字面量的介绍
对象字面量提供了一个包括行为的方式去组织代码,这也意味着避免污染全局命名空间,这是对于一个较大项目的很好做法,它迫使你去思考你的代码在一开始就应该做什么以及哪些部分需要放置在合适的位置。对象字面量是封装相关行为的方式,如下所示:
var myObjectLiteral = {myBehavior1 : function() {/* do something */ },myBehavior2 : function() {/* do something else*/} };
假设你使用jQuery完成一个点击list项显示和隐藏的功能:
$(document).ready(function() {$('#myFeature li').append('<div/>').each(function(){$(this).find('div').load('foo.php?item=' + $(this).attr('id'));}).click(function() {$(this).find('div').show();$(this).siblings().find('div').hide();}); });
就是这么简单,但是当你想在这个例子中改变一些需求,例如加载内容的URL的方式,以及加载内容的URL,或者是显示和隐藏的行为等等,对象字面量清晰地划分了这些功能特征,看起来如下:
var myFeature = {config : {wrapper : '#myFeature',container : 'div',urlBase : 'foo.php?item='},init : function(config){$.extend(myFeature.config, config);$(myFeature.config.wrapper).find('li').each(function(){myFeature.getContent($(this));}).click(function(){myFeature.showContent($(this));});},buildUrl : function($li){return myFeature.config.urlBase + $li.attr('id');},getContent : function($li){$li.append('<' + myFeature.config.container + '/>');var url = myFeature.buildUrl($li);$li.find(myFeature.config.container).load(url);},showContent : function($li){$li.find(myFeature.config.container).show();myFeature.hideContent($li.siblings());},hideContent : function($elements){$elements.find(myFeature.config.container).hide();} };$(document).ready(function() { myFeature.init(); });
最初的例子是很简单的,用对象字面量形式却让代码变得更长,说实话,对象字面量形式一般是不会节省你的代码量的。使用对象字面量我们将代码的逻辑部分分割开来,因此很容易找到我们想要改变的部分,我们已经取得我们的功能扩展,提供了覆写默认配置的功能。并且做了文档上的限制,很容易一眼看出该部分做什么功能。抛开这个例子的简单结构,随着需求的增长我们的代码结构将变得愈来愈清晰。
An in-depth example 一个更深层次的示例
我们的任务是创建每个部分含有多项内容的UI元素,点击一个区块将显示区块中项目的列表,点击项目列表中的项目,项目内容将显示在内容区域。每当区块被显示时,第一个项目列表应该也被显示。第一部分应该在页面加载时被显示。
作者想表达的效果图应该是这样的:
Step 1: HTML结构
编写良好语义化的HTML是编写好的JavaScript的先决条件,所以我们思考一下HTML应该长什么样子呢,HTML应该是:
- 当JavaScript不可用时HTML仍然有意义并且很好的工作
- 提供可预测的DOM结构方便附加在JavaScript上
- 避免不必要的IDs和classes(你可能会感到惊讶)
考虑到这些策略,我们开始编写html吧:
<h1>This is My Nifty Feature</h1><div id="myFeature"><ul class="sections"><li><h2><a href="/section/1">Section 1</a></h2><ul><li><h3><a href="/section/1/content/1">Section 1 Title 1</a></h3><p>The excerpt content for Content Item 1</p></li><li><h3><a href="/section/1/content/2">Section 1 Title 2</a></h3><p>The expert content for Content Item 2</p></li><li><h3><a href="/section/1/content/3">Section 1 Title 3</a></h3><p>The expert content for Content Item 3</p></li></ul></li><li><h2><a href="/section/2">Section 2</a></h2><ul><li><h3><a href="/section/2/content/1">Section 2 Title 1</a></h3><p>The excerpt content for Content Item 1</p></li><li><h3><a href="/section/2/content/2">Section 2 Title 2</a></h3><p>The expert content for Content Item 2</p></li><li><h3><a href="/section/2/content/3">Section 2 Title 3</a></h3><p>The expert content for Content Item 3</p></li></ul></li><li><h2><a href="/section/3">Section 3</a></h2><ul><li><h3><a href="/section/3/content/1">Section 3 Title 1</a></h3><p>The excerpt content for Content Item 1</p></li><li><h3><a href="/section/3/content/2">Section 3 Title 2</a></h3><p>The expert content for Content Item 2</p></li><li><h3><a href="/section/3/content/3">Section 3 Title 3</a></h3><p>The expert content for Content Item 3</p></li></ul></li></ul> </div>
注意此时没有任何标记显示一级导航或二级(项目)导航,通过加入Jquery让它们工作;不支持JavaScript的用户将会得到很好的语义HTML(如果HTML表达语义不清,应该是时候替换旧的语义和实现渐进增强了)。
Step 2: Scaffolding the Object Object的脚手架
创建对象第一步是为对象创建"存根",可以把"存根"想象成占位符;它们是我们要构建的功能大纲,我们的对象将有如下方法:
- myFeature.init() 将会运行在 $(document).ready() 中。通过语义化的HTML标签,让我们很快进入到JavaScript的用户界面。
- myFeature.buildSectionNav() 将被 myFeature.init() 调用。这将需要一个jQuery对象包含所有section语义的HTML标签来构建一级导航,每项一级导航而且会被绑定点击事件,点击它们将会显示相应的部分(二级导航)
- myFeature.buildItemNav() 将被 myFeature.showSection() 调用,这需要jQuery对象包含所有和section相关的具有语义HTML的item项,用它们来建立二级导航。它们(二级导航)也将会被绑定点击事件所以点击后将显示相关内容。
- myFeature.showSection() 当用户点击一级导航的项时将被调用。通过一级导航的点击项判断哪部分语义内容将被显示。
- myFeature.showContentItem() 当用户点击二级导航时将被调用。通过二级导航的点击判断那部分语义内容将被显示。
首先配置属性,通过 myFeature.config 将各个属性设置到一起而不是在代码中各个部分定义。我们将在 myFeature.init() 中提供默认属性覆写的功能。
var myFeature = {'config' : {},'init' : function() {},'buildSectionNav' : function() {},'buildItemNav' : function() {},'showSection' : function() {},'showContentItem' : function() {} };
Step 3: The Code 代码
一旦我们建立起了骨架,是时候开始编写后面的代码了。首先编写 myFeature.config 对象和 myFeature.init() 方法:
'config' : {//default container is #myFeature'container' : $('#myFeature') },'init' : function(config){//provide for custom configuration via init()if(config && typeof(config) == 'object' ){$.extend(myFeature.config, config);}//create and/or cache some DOM elements//we'll want to use throughout the codemyFeature.$container = myFeature.config.container;myFeature.$sections = myFeature.$container.// only select immediate children!find('ul.sections > li'); myFeature.$items = myFeature.$sections. find('ul > li');myFeature.$section_nav = $('<p/>').attr('id', 'section_nav').prependTo(myFeature.$container);myFeature.$item_nav = $('<p/>').attr('id', 'item_nav').insertAfter(myFeature.section_nav);myFeature.$content = $('<p/>').attr('id', 'content').insertAfter(myFeature.$item_nav);//build the section-level nav and //"click" the first item myFeature.buildSectionNav(myFeature.$sections);myFeature.$section_nav.find('li:first').click();//hide the plain HTML from sightmyFeature.$container.find('ul.sections').hide();//make a note that the initialization//is complete; we don't strictly need this //for this iteration, but it can come in handy myFeature.initialized = true; }
接下来编写 myFeature.buildSectionNav() 方法:
'buildSectionNav' : function($sections){//iterate over the provided list of sections$sections.each(function(){//get the sectionvar $section = $(this);//create a list item for the section navigation$('<li/>')//use the text of the first h2//in the section as the text for//the section navigation.text($section.find('h2:first').text())//add the list item to the section navigation .appendTo(myFeature.$section_nav)//use data() to store a reference//to the original section on the //newly-created list item.data('section', $section)//bind the click behavior//to the newly created list item//so it will show the section .click(myFeature.showSection); }); }
接下来编写 myFeature.buildItemNav() 方法:
'buildItemNav' : function($items){//iterate over the provided list of items$items.each(function(){//get the itemvar $item = $(this);//create a list item element for the //item navigation$('<li/>')//use the text of the first h3//in the item as the text for the //item navigation.text($item.find('h3:first').text())//add the list item to item navigation .appendTo(myFeature.$item_nav)//use data to store a reference //to the original item on the //newly created list item.data('item', $item)//bind the click behavior to the //newly created list item so it will //show the content item .click(myFeature.showContentItem);}) }
最后,我们将编写 showSection() 和 showContentItem() 方法:
'showSection' : function(){// capture the list item that was clicked onvar $li = $(this);//clear out the left nav and content area myFeature.$item_nav.empty();myFeature.$content.empty();//get the jQuery section object from original HTML,//which we stored using data() during buildSectionNavvar $section = $li.data('section');//mark the clicked list item as current//and remove the current marker from its siblings$li.addClass('current').siblings().removeClass('current');//find all of items related to the sectionvar $items = $section.find('ul li');//build the item nav for the section myFeature.buildItemsNav($items);//"click" on the first list item in the section's item nav myFeature.$item_nav.find('li:first').click(); },'showContentItem' : function(){var $li = $(this);//mark the clicked list item as current//and remove the current marker form its siblidngs$li.addClass('current').siblings().removeClass('current');//get the jQuery item object from the original HTML,//which we stored using data during buildContentNavvar $item = $li.data('item');myFeature.$content.html($item.html()); }
所有准备完后,我们开始调用 myFeature.init() 方法:
$(document).ready(myFeature.init())
Step 4: Changing Requirements
没有项目是不提需求的,随时变更是特点不是吗?对象字面量的方式使开发快速并且相当容易实现变更需求。如果我们需要获取内容片段是从AJAX得来的而不是HTML?假设这里添加了前后端交互的功能,尝试一下:
var myFeature = {'config' : {'container' : $('#myFeature'),// configurable function for getting// a URL for loading item content'getItemURL' : function($item){return $item.find('a:first').attr('href');}},'init' : function (config) {// stays the same },'buildSectionNav' : function($sections){// stays the same },'buildItemNav' : function($items) {// stays the same },'showSection' : function(){//stays the same },'showContentItem' : function(){var $li = $(this);$li.addClass('current').$siblings().removeClass('current');var $item = $li.data('item');var url = myFeature.config.getItemURL($item);// myFeature.$content.html($item.html()) myFeature.$content.load(url);}}
想要更加灵活吗?有许多你能配置的(覆写)如果你真的想使代码功能变得灵活。例如,你可以通过配置 myFeature.config 自定义地为每个item找到对应的文本:
var myFeature = {'configure' : {' container' : $('#myFeature'),//specify the default selector// for finding the text to use// for each item in the item nav'itemNavSelector' : 'h3',//specify a default callback//for "processing" the jQuery object//returned by the itemNavText selector'itemNavProcessor' : function($selection){return 'Preview of ' + $selection.eq(0).text();}},'init' : function(config){// stays the same },'buildSectionNav' : function($sections){// stays the same },'buildItemNav' : function($items){$.items.each(function(){var $item = $(this);//use the selector and processor//from the config//to get the text for each item navvar myText = myFeature.config.itemNavProcessor($item.find(myFeature.config.itemNavSelector));$('<li/>')//use the new variable//as the text for the nav item .text(myText).appendTo(myFeature.$item_nav).data('item', $item).click(myFeature.showContentItem);});},'showSection' : function(){// stays the same },'showContentItem' : function (){// stays the same } };
只要你添加配置对象参数,调用 myFeature.init() 时就可以覆写config对象:
$(document).ready(function(){myFeature.init({ 'itemNavSelector' : 'h2' }); });
OK!有了以上了解和学习,读者们可以尝试实现jQuery history 插件~
Conclusion 总结
如果你按照代码例子一步步理解过来后,你应该对对象字面量有了基本了解,它会对你开发复杂功能和交互提供一个有用的方式,提供给你可以在本代码上继续扩展功能,我鼓励你在JavaScript中尝试使用对象字面量模式去代替短短几行的代码——因为这会迫使你去思考元素的表现和行为去构成一个复杂的功能或交互。一旦你掌握了它,它为扩展和重用你的代码提供了坚实的基础。
Learn More 了解更多
- More on the jQuery data() method
- More praise for the object literal pattern
- The jQuery History plugin
- An interseting application of the object literal pattern for architecting code for multiple page types
附录前文中An in-depth example 完整代码:
<!DOCTYPE html> <html> <head><meta charset="utf-8"><title>An in-depth example 一个更深层次的示例</title><style type="text/css">.current{background: #f47460;}</style><script type="text/javascript" src="http://cdn.bootcss.com/jquery/3.0.0/jquery.min.js"></script> </head> <body> <h1>This is My Nifty Feature</h1><div id="myFeature"><ul class="sections"><li><h2><a href="/section/1">Section 1</a></h2><ul><li><h3><a href="/section/1/content/1">Section 1 Title 1</a></h3><p>The excerpt content for Content Item 1</p></li><li><h3><a href="/section/1/content/2">Section 1 Title 2</a></h3><p>The expert content for Content Item 2</p></li><li><h3><a href="/section/1/content/3">Section 1 Title 3</a></h3><p>The expert content for Content Item 3</p></li></ul></li><li><h2><a href="/section/2">Section 2</a></h2><ul><li><h3><a href="/section/2/content/1">Section 2 Title 1</a></h3><p>The excerpt content for Content Item 1</p></li><li><h3><a href="/section/2/content/2">Section 2 Title 2</a></h3><p>The expert content for Content Item 2</p></li><li><h3><a href="/section/2/content/3">Section 2 Title 3</a></h3><p>The expert content for Content Item 3</p></li></ul></li><li><h2><a href="/section/3">Section 3</a></h2><ul><li><h3><a href="/section/3/content/1">Section 3 Title 1</a></h3><p>The excerpt content for Content Item 1</p></li><li><h3><a href="/section/3/content/2">Section 3 Title 2</a></h3><p>The expert content for Content Item 2</p></li><li><h3><a href="/section/3/content/3">Section 3 Title 3</a></h3><p>The expert content for Content Item 3</p></li></ul></li></ul> </div> <script type="text/javascript"> var myFeature = {'config': {'container' : $('#myFeature')},'init': function(config){if(config && typeof config == 'object'){$.extend(myFeature.config, config);}//缓存变量 myFeature.$container = myFeature.config.container;myFeature.$sections = myFeature.$container.find('ul.sections > li');myFeature.$items = myFeature.$sections.find('ul > li');myFeature.$section_nav = $('<p/>').attr('id', 'section_nav').prependTo(myFeature.$container);myFeature.$item_nav = $('<p/>').attr('id', 'item_nav').insertAfter(myFeature.$section_nav);myFeature.$content = $('<p/>').attr('id', 'content').insertAfter(myFeature.$item_nav);//初始化新增的这三层DOM结构 myFeature.buildSectionNav(myFeature.$sections);myFeature.$section_nav.find('li:first').click();//隐藏原有的HTML结构 myFeature.$container.find('ul.sections').hide();},'buildSectionNav' : function($sections){//绑定事件 $sections.each(function(){var $section = $(this);$('<li>').text($section.find('h2:first').text()).appendTo(myFeature.$section_nav).data('section', $section).click(myFeature.showSection)});},'buildItemNav' : function($items){//绑定事件 $items.each(function(){var $item = $(this);$('<li>').text($item.find('h3:first').text()).appendTo(myFeature.$item_nav).data('item', $item).click(myFeature.showContentItem);});},'showSection' : function(){//事件处理程序var $li = $(this);myFeature.$item_nav.empty();myFeature.$content.empty();var $section = $li.data('section');$li.addClass('current').siblings().removeClass('current');var $items = $section.find('ul li');myFeature.buildItemNav($items);myFeature.$item_nav.find('li:first').click();},'showContentItem' : function(){//事件处理程序var $li = $(this);$li.addClass('current').siblings().removeClass('current');var $item = $li.data('item');myFeature.$content.html($item.html());} }$(document).ready(function(){myFeature.init()}); </script> </body> </html>
View Code
转载于:https://www.cnblogs.com/venoral/p/5639201.html
【译】Using Objects to Organize Your Code相关推荐
- 【译】如何提出好的Code Review反馈
没错,Code Review系列还在继续,今天我们一起来聊一聊如何提出好的Code Review反馈. Code Review是保证代码的质量和可维护性,以及向团队成员分享知识的重要手段.但是,随着团 ...
- 大型Javascript应用架构的模式(译文)
附上翻译好的word文件 http://files.cnblogs.com/lizhug/Patterns_For_Large-Scale_JavaScript_Application_Archite ...
- Serializing Lua objects into Lua Code
The following little snippet allows you to 'pickle' Lua objects directly into Lua code (with the exc ...
- 【转】(译)iOS Code Signing: 解惑详解
原文链接地址:http://www.cnblogs.com/andyque/archive/2011/08/30/2159086.html 教程截图: 下面是一篇有澳洲墨尔本的一名全职iOS开发者提供 ...
- mysql autoenlist默认_javascript code all (2) (转转)
1. οncοntextmenu="window.event.returnvalue=false" 将彻底屏蔽鼠标右键 可用于Table 2. 取消选取.防止复制 3. οnpas ...
- Objects.requireNonNull 方法说明
在写代码的时候,Idea经常会提醒我们可以使用这个方法来进行参数非空检查, 这个方法的源码也非常简单, 如下所示: /*** Checks that the specified object refe ...
- Google Code Review最新指南
本文译自Google最新开放的code review指南:How to do a code review 原文地址:https://google.github.io/eng-practices/rev ...
- 带有Node,React和Redux 1的Retrogames库:服务器API和React前端
2017/03/29 Update: Fixed the versions of react-router and react-hot-loader. 2017/03/20 Update: Webpa ...
- 电子界卡组构建2019_2018–2019年构建现代Android应用程序的路线图
电子界卡组构建2019 Kriptofolio应用程序系列-简介 (Kriptofolio app series - Introduction) Welcome to this series of b ...
最新文章
- 基于pytorch后量化(mnist分类)---浮点训练vs多bit后量化vs多bit量化感知训练效果对比
- 多线程模式(2):Guarded Suspension模式
- Java Character 类
- [云炬创业管理笔记]第三章测试5
- python中str用法_python中的str()不能直接用吗
-问答-阿里云开发者社区-阿里云
- solr java api_solr java api
- 什么是工业级交换机?工业交换机作用有哪些?
- 第十篇:Spring Boot整合mybatis+Mysql 入门试炼02
- OSChina 周五乱弹 ——发现办公室女同事走光了
- ZetCode PHP Symfony 教程
- PowerShell 方式部署Sharepoint Solution
- 2017乌鲁木齐ICPC: K. Sum of the Line(容斥)
- OpenCV-Python教程(8、Canny边缘检测)
- python运维知识大全_python基础知识
- 第五章 Spark-SQL进阶(一)之Dataset用法
- wps小技巧,wps删除空白页怎么删?
- dell进入u盘启动模式_Dell戴尔笔记本bios设置u盘启动详细步骤
- MC9S12XS128 事件处理
- ios描述文件的申请
- 2013中国互联网公司、全球互联网公司最新市值排名(2013.04.20)
热门文章
- Python学习(四) —— 编码
- jquery append 动态添加的元素事件on 不起作用的解决方案
- 使用CXF 2.7.5出现的java.lang.RuntimeException: Cannot create a secure XMLInputFactory错误解决...
- 数据库(概念、语法、DBMS、SQL语言:创建数据库、表格,添加、修改、删除数据记录)...
- Scrum 项目7.0
- [总结]Jquery api 快速参考
- 汉字转拼音缩写的函数以及其他函数
- SQL 语法参考手册
- spring cloud微服务分布式云架构(四)-断路器(Hystrix)
- 通过人与人的交互,反思软件系统与软件系统之间的集成交互问题