【译】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的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: linux 学习 常用命令
- 下一篇: MonkeyRunner 实现自动点击截
