Upload
rebecca-murphey
View
10.295
Download
3
Embed Size (px)
Citation preview
Cleaner, Leaner, MeanerRefactoring your JavaScript
Rebecca Murphey • @rmurphey • rebeccamurphey.com
Thursday, December 2, 2010
http://github.com/rmurphey/refactor-jquery
2Thursday, December 2, 2010
“... changing a software system in such a way that it does not alter the external behavior of the code yet improves its internal structure.”
Martin Fowler, “Refactoring”
3Thursday, December 2, 2010
Hint: Not just because it’s fun.
Why Refactor?Thursday, December 2, 2010
“When you sit down and solve a problem, that solution is merely a !rst draft.”
Stoyan Stefanov, “JavaScript Patterns”
5Thursday, December 2, 2010
Internal rewards
Increase maintainability
Increase extensibility & reusability
User experience rewards
Improve page performance
Increase testability (reduce bugs)
6Thursday, December 2, 2010
Put another way: Refactoring will make your users happier, make your code cheaper to work with, or both.
7Thursday, December 2, 2010
8 tell-tale signs your code needs some TLC
JavaScriptCode Smells
Thursday, December 2, 2010
HTML in your JavaScript
$('#showMessage').click(function() { $('<div>' + '<h1>' + $('#messageTitle').val() + '</h1>' + '<p>' + $('#messageText').val() + '</p>' + '</div>') .appendTo('#messageContainer')});
// MINTY FRESH: Use templates instead<script type="text/x-jquery-tmpl" id="messageTemplate"> <div> <h1>${title}</h1> <p>${text}</p> </div></script>
$('#messageTemplate').template('messageTemplate');
$.tmpl('messageTemplate', { title : $('#messageTitle').val(), text : $('#messageText').val()}).appendTo('#messageContainer');
9Thursday, December 2, 2010
http://api.jquery.com/category/plugins/templates/
http://github.com/janl/mustache.js/
http://documentcloud.github.com/underscore/
10Thursday, December 2, 2010
Changing style information in JavaScript
$('p.special').click(function() { $(this).css({ 'color' : 'red', 'font-weight' : 'bold' });})
// MINTY FRESH: Keep presentation information in CSSp.extraSpecial { color: red; font-weight: bold;}
$('p.special').click(function() { $(this).addClass('extraSpecial');});
11Thursday, December 2, 2010
Duplication of existing jQuery methods
function isItemInArray(item, arr) { var inArray = false, len = arr.length;
for (var i = 0; i < len; i++) { if (item == arr[i]) { inArray = true; } } return inArray;}
// MINTY FRESH: Use jQuery!function isItemInArray(item, arr) { return $.inArray(item, arr) > -1;}
12Thursday, December 2, 2010
http://api.jquery.com/category/utilities/
http://api.jquery.com/category/miscellaneous/
13Thursday, December 2, 2010
Repetition that jQuery lets you avoid
$('a.thinger').each(function() { $(this).attr('href', $(this).attr('href') + '?ajax=true');});$('a.thinger').hide();$('#myButton').click(function(){ $('a.thinger').show();})
// MINTY FRESH: Use the chain and setter functions!var thingers = $('a.thinger'), // store selection in a var button = $('#myButton'); // just in case!
thingers.attr('href', function(idx, oldVal) { // pass a setter function & avoid the need // to iterate over matches return oldVal + '?ajax=true';}).hide();
button.click(function() { thingers.show();});
14Thursday, December 2, 2010
Deeply nested anonymous functions
$(document).ready(function() { $('#enableAwesome').click(function() { $('ul.foo li').each(function() { var li = $(this);
li.data('name', li.html()) .find('a.remove').click(function(e) { $.ajax({ url : $(this).attr('href'), dataType : 'json', type : 'post', success : function(resp) { if (resp.ok) { li.remove(); } }, error : console.log }) e.preventDefault(); }); }) });});
15Thursday, December 2, 2010
// MINTY FRESH: Isolate functionality into an object with methodsvar awesome = { enableListItem : function() { var li = $(this); li.data('name', li.html()); }, removeListItem : function() { var a = $(this), li = a.closest('li');
awesome.removeOnServer({ url : a.attr('href'), success : function(resp) { if (resp.ok) { li.remove(); } } }); }, removeOnServer : function (config) { var defaults = { type : 'post', dataType : 'json', error : console.log }, settings = $.extend({}, defaults, config);
if (!settings.url) { return; } $.ajax(config); }};
16Thursday, December 2, 2010
$(document).ready(function() { $('#enableAwesome').click(function() { $('ul.foo li') .each(awesome.enableListItem) .delegate('a.remove', 'click', awesome.removeListItem); });});
17Thursday, December 2, 2010
Overtesting for truthiness
// SMELLY: Overtesting for truthinessif (errorMsg != null && errorMsg.length > 0) { // ...}
// MINTY FRESH: Be as terse as you canif (errorMsg && errorMsg.length) { // ...}
18Thursday, December 2, 2010
// SMELLYif (total == null || total == "0") { // ...}
// MINTY FRESHif (!parseInt(total, 10)) { // ... }
19Thursday, December 2, 2010
// SMELLYif (price == null) { // ...} else if(discountPrice != null && price == discountPrice) { // ...}
// MINTY FRESHif (!price) { // ...
// we already know that price isn't null,// so why test if discountPrice is? if it's// equal to price, we know it's not null} else if (price == discountPrice) { // ...}
20Thursday, December 2, 2010
Repetitive logic blocks
function isItABigNumber(num) { if(num > 5000) { $('#myContainer').html('<p>It was a big number</p>'); $('#myInput').val(num); $('.thinger').hide(); } else { $('#myContainer').html('<p>It was not a big number</p>'); $('#myInput').val(''); $('.thinger').show(); }}
// MINTY FRESH: Only repeat what needs repeatingfunction isItABigNumber(num) { var big = num > 5000; $('#myContainer').html(big ? '<p>It was a big number</p>' : '<p>It was not a big number</p>'); $('#myInput').val(big ? num : ''); $('.thinger')[big ? 'hide' : 'show']();}
21Thursday, December 2, 2010
Passing a lot of arguments to a function
function crazyConcatenation(selector, word1, word2, word3, repeat) { var arr = [], words = [], joinedWords; if (selector == null) { return; } if (word1 == null) { return; } if (word2 != null) { words.push(word2); } if (word3 != null) { words.push(word3); } if (!repeat) { repeat = 5; } joinedWords = words.join(', '); while (repeat--) { arr.push(joinedWords); } $(selector).html(arr.join('<br/>'))}
crazyConcatenation('#foo', 'Hello', null, null, 5);
22Thursday, December 2, 2010
// MINTY FRESH: Using an object insteadfunction crazyConcatenation(config) { // indicate clearly what's required if ( !config.selector || !config.words || !config.words.length ) { return; } var defaults = { repeat : 5 }, settings = $.extend({}, defaults, config), joinedWords = settings.words.join(', '); while (settings.repeat--) { arr.push(joinedWords); } $(settings.selector).html(arr.join('<br/>'))}
crazyConcatenation({ selector : '#foo', words : [ 'foo', 'bar', 'baz' ], repeat : 20});
23Thursday, December 2, 2010
Advanced MovesCommon patterns for improving your code
Thursday, December 2, 2010
“Writing to be read means writing code ... with the idea that someone else will read it. is fact alone will make you edit and think of better ways to solve the problem you have at hand.”
Stoyan Stefanov, “JavaScript Patterns”
25Thursday, December 2, 2010
example add the same behavior to similar content without depending on IDs
26Thursday, December 2, 2010
example cache XHR responses, and create an API to a server-side service
27Thursday, December 2, 2010
example refactor a portlet to use the jQuery UI widget factory
28Thursday, December 2, 2010
rebeccamurphey.com
blog.rebeccamurphey.com
@rmurphey
http://github.com/rmurphey/refactor-jquery
http://pinboard.in/u:rmurphey/t:refactor/
Presented at the 2010 Rich Web Experience
29Thursday, December 2, 2010