├── README.md ├── docs └── refactoring-structure.md └── patterns ├── general ├── extract-method.md ├── introduce-explaining-variable.md └── replace-temp-with-query.md └── javascript ├── declare-block-scope-variables-with-let.md ├── extract-module.md ├── replace-aggregate-property-operations-with-reduce.md ├── replace-context-variable-with-bind-call-apply.md ├── replace-default-param-logic-with-es6-default-params.md ├── replace-direct-array-access-element-updates-with-map.md ├── replace-for-with-foreach.md ├── replace-jquery-selector-with-query-selector.md └── replace-switch-with-object-literal.md /README.md: -------------------------------------------------------------------------------- 1 | JavaScript Refactoring Patterns 2 | ======================= 3 | A collection of refactoring patterns for improving the design of existing JavaScript code. 4 | 5 | This work is greatly motivated by, and many patterns are adapted from, Martin Fowler's excellent book Refactoring - Improving the design of existing code. 6 | 7 | # Overview of Refactoring Patterns 8 | 9 | ## General Patterns 10 | These refactorings are ported to JavaScript from Martin Fowler's book Refactoring - Improving the design of existing code. Although they are not JavaScript specific they are still great refactorings which can help to improve the design of JavaScript code. 11 | 12 | ### Composing Methods 13 | - [Extract function/method](https://github.com/KarlPurk/javascript-refactoring/blob/master/patterns/general/extract-method.md) 14 | - [Replace temp with query](https://github.com/KarlPurk/javascript-refactoring/blob/master/patterns/general/replace-temp-with-query.md) 15 | - [Introduce explaining variable](https://github.com/KarlPurk/javascript-refactoring/blob/master/patterns/general/introduce-explaining-variable.md) 16 | - Split temporary variable 17 | - Remove assignments to parameters 18 | 19 | ## JavaScript Patterns 20 | These refactorings are JavaScript specific and should help to improve the design of your code. The refactorings are grouped by the ECMAScript version that implements the functionality that they depend on. 21 | 22 | ### ECMAScript 3 23 | - [Replace switch with object literal](https://github.com/KarlPurk/javascript-refactoring/blob/master/patterns/javascript/replace-switch-with-object-literal.md) 24 | - [Extract module](https://github.com/KarlPurk/javascript-refactoring/blob/master/patterns/javascript/extract-module.md) 25 | 26 | ### ECMAScript 5 27 | - [Replace for with forEach](https://github.com/KarlPurk/javascript-refactoring/blob/master/patterns/javascript/replace-for-with-foreach.md) 28 | - [Replace aggregate property operations with reduce](https://github.com/KarlPurk/javascript-refactoring/blob/master/patterns/javascript/replace-aggregate-property-operations-with-reduce.md) 29 | - [Replace direct array access element updates with map](https://github.com/KarlPurk/javascript-refactoring/blob/master/patterns/javascript/replace-direct-array-access-element-updates-with-map.md) 30 | - [Replace context variable with bind/call/apply](https://github.com/KarlPurk/javascript-refactoring/blob/master/patterns/javascript/replace-context-variable-with-bind-call-apply.md) 31 | 32 | ### ECMAScript 6 33 | - [Declare block scope variables with let](https://github.com/KarlPurk/javascript-refactoring/blob/master/patterns/javascript/declare-block-scope-variables-with-let.md) 34 | - [Replace default param logic with ES6 default params](https://github.com/KarlPurk/javascript-refactoring/blob/master/patterns/javascript/replace-default-param-logic-with-es6-default-params.md) 35 | - Replace nested async callbacks with promises 36 | 37 | ### jQuery 38 | - [Replace jQuery selector with querySelector/querySelectorAll](https://github.com/KarlPurk/javascript-refactoring/blob/master/patterns/javascript/replace-jquery-selector-with-query-selector.md) 39 | -------------------------------------------------------------------------------- /docs/refactoring-structure.md: -------------------------------------------------------------------------------- 1 | Refactoring Structure 2 | ====================== 3 | 4 | Each refactoring should contain the following: 5 | 6 | - Name 7 | - Summary 8 | - Motivation 9 | - Mechanics 10 | - Examples 11 | - More Information 12 | 13 | Most refactorings will probably start with only a few of these sections. Other sections can be added at a later date. At the very least each refactoring should have a name and summary. 14 | 15 | # Name 16 | The name of the refactoring should be short, clear and memorable. It should describe the refactoring at a high level. 17 | 18 | #Summary 19 | The summary section should describe, as briefly as possible, the situation the user is in before the refactoring and the result of performing the refactoring. This section should also include two brief accompanying code examples. 20 | 21 | #Motivation 22 | The motivation section should describe the situations where a user should and should not perform the refactoring. 23 | 24 | #Mechanics 25 | The mechanics section should describe the steps a user needs to take to perform the refactoring. 26 | 27 | #Examples 28 | The examples section should include detailed examples of the refactoring in action. Multiple examples may be required for refactorings that have multiple implementations. 29 | 30 | #More Information 31 | The more information section should list any related resources that may be useful for the user implementing the pattern. 32 | -------------------------------------------------------------------------------- /patterns/general/extract-method.md: -------------------------------------------------------------------------------- 1 | Extract method/function 2 | ======================== 3 | 4 | You have a code fragment that can be grouped together. 5 | 6 | ```javascript 7 | var posts = getPosts(); 8 | posts.forEach(function(post) { 9 | console.log(post); 10 | }); 11 | ``` 12 | 13 | Extract the fragment into a new function/method. 14 | 15 | ```javascript 16 | var logPosts = function(posts) { 17 | posts.forEach(function(post) { 18 | console.log(post); 19 | }); 20 | }; 21 | 22 | var posts = getPosts(); 23 | logPosts(posts); 24 | ``` 25 | 26 | # More Information 27 | 28 | - Martin Fowler, Refactoring - Improving the design of existing code (Addison-Wesley, 2007), p.110. 29 | - http://refactoring.com/catalog/extractMethod.html 30 | -------------------------------------------------------------------------------- /patterns/general/introduce-explaining-variable.md: -------------------------------------------------------------------------------- 1 | Introduce Explaining Variable 2 | ============================== 3 | 4 | You have a complicated expression that can be simplified. 5 | 6 | ```javascript 7 | if (device.platform.toUpperCase() === 'IOS' && 8 | device.platform.version < 7 && 9 | device.browser.toUpperCase() === 'CHROME') { 10 | ``` 11 | 12 | Simplify the expression by introducing variables that clearly explain each condition: 13 | 14 | ```javascript 15 | var isTargetPlatform = device.platform.toUpperCase() === 'IOS'; 16 | var isTargetVersion = device.platform.version < 7 17 | var isTargetBrowser = device.browser.toUpperCase() === 'CHROME'; 18 | 19 | if (isTargetPlatform && isTargetVersion && isTargetBrowser) { 20 | ``` 21 | 22 | # More Information 23 | 24 | - http://refactoring.com/catalog/extractVariable.html 25 | - https://sourcemaking.com/refactoring/introduce-explaining-variable 26 | -------------------------------------------------------------------------------- /patterns/general/replace-temp-with-query.md: -------------------------------------------------------------------------------- 1 | Replace temp with query 2 | ======================== 3 | You’re storing the result of an expression in a temporary variable. 4 | 5 | ```javascript 6 | var price = items.getPrice() + shipping.getTax(); 7 | updateTotalPrice(price); 8 | ``` 9 | 10 | Replace the temporary variable with a query. 11 | 12 | ```javascript 13 | var getTotalPrice = function(items, shipping) { 14 | return items.getPrice() + shipping.getTax(); 15 | } 16 | updateTotalPrice(getTotalPrice(items, shipping)); 17 | ``` 18 | 19 | # More Information 20 | - Martin Fowler, Refactoring - Improving the design of existing code (Addison-Wesley, 2007), p.120. 21 | - http://refactoring.com/catalog/replaceTempWithQuery.html 22 | -------------------------------------------------------------------------------- /patterns/javascript/declare-block-scope-variables-with-let.md: -------------------------------------------------------------------------------- 1 | # Declare block scope variables with let 2 | 3 | You're using `var` to declare variables within a block: 4 | 5 | ```javascript 6 | 7 | var i, max = 10; 8 | 9 | for (i = 0; i < max; i++) { 10 | var timesLooped = i + 1; 11 | console.log('Times looped:', timesLooped); 12 | } 13 | 14 | ``` 15 | 16 | Use ES6 `let` instead: 17 | 18 | ```javascript 19 | 20 | var i, max = 10; 21 | 22 | for (i = 0; i < max; i++) { 23 | let timesLooped = i + 1; 24 | console.log('Times looped:', timesLooped); 25 | } 26 | 27 | ``` 28 | 29 | 30 | # More Information 31 | 32 | - https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/let 33 | - https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/var 34 | -------------------------------------------------------------------------------- /patterns/javascript/extract-module.md: -------------------------------------------------------------------------------- 1 | Extract module 2 | =============== 3 | You have a collection of related functions that can be utilised through a simple interface. 4 | 5 | ```javascript 6 | var getPostsFromDataSource = function() { 7 | // Fetch posts from data source 8 | }; 9 | var logPosts = function(posts) { 10 | // Log each post to console 11 | }; 12 | var mapPosts = function(posts) { 13 | // Map each post to a model 14 | }; 15 | var getPosts = function() { 16 | var posts = getPostsFromDataSource(); 17 | posts = mapPosts(posts); 18 | logPosts(posts); 19 | return posts; 20 | }; 21 | var posts = getPosts(); 22 | ``` 23 | 24 | Encapsulate the functions in a new function and return a fine-grained interface for clients. 25 | 26 | ```javascript 27 | var postsModule = function() { 28 | var getPostsFromDataSource = function() { 29 | // Fetch posts from data source 30 | }; 31 | var logPosts = function(posts) { 32 | // Log each post to console 33 | }; 34 | var mapPosts = function(posts) { 35 | // Map each post to a model 36 | }; 37 | var getPosts = function() { 38 | var posts = getPostsFromDataSource(); 39 | posts = mapPosts(posts); 40 | logPosts(posts); 41 | return posts; 42 | }; 43 | return { 44 | getPosts: getPosts 45 | }; 46 | }(); 47 | var posts = postsModule.getPosts(); 48 | ``` 49 | 50 | # More Information 51 | - http://addyosmani.com/largescalejavascript/#modpattern 52 | -------------------------------------------------------------------------------- /patterns/javascript/replace-aggregate-property-operations-with-reduce.md: -------------------------------------------------------------------------------- 1 | Replace aggregate property operations with reduce 2 | ================================================== 3 | You’re iterating a collection to perform an aggregate query, using a temporary variable to record the results. 4 | 5 | ```javascript 6 | var result = 0; 7 | [1, 2, 3].forEach(function(item) { 8 | result += item; 9 | }); 10 | console.log(result); 11 | ``` 12 | 13 | Remove the temporary variable and use the ECMAScript 5 reduce method instead. 14 | 15 | ```javascript 16 | var result = [1, 2, 3].reduce(function(prev, cur) { 17 | return prev + cur; 18 | }, 0); 19 | console.log(result); 20 | ``` 21 | 22 | # More Information 23 | 24 | - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach 25 | - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce 26 | -------------------------------------------------------------------------------- /patterns/javascript/replace-context-variable-with-bind-call-apply.md: -------------------------------------------------------------------------------- 1 | Replace context variable with bind/call/apply 2 | ============================================= 3 | You're using a temporary variable to store a reference to an object so that you can maintain scope in an inner function. 4 | 5 | ```javascript 6 | Post.prototype.getDateTime = function() { 7 | var self = this, 8 | concatDateTime = function() { 9 | return self.date + ‘ ‘ + self.time; 10 | }; 11 | return concatDateTime(); 12 | }; 13 | ``` 14 | 15 | Remove the temporary variable and use ECMAScript 5 bind instead. 16 | 17 | ```javascript 18 | Post.prototype.getDateTime = function() { 19 | var concatDateTime = function() { 20 | return this.date + ‘ ‘ + this.time; 21 | }.bind(this); 22 | return concatDateTime(); 23 | }; 24 | ``` 25 | 26 | Remove the temporary variable and use the ECMAScript 3 call/apply instead. 27 | 28 | ```javascript 29 | Post.prototype.getDateTime = function() { 30 | var concatDateTime = function() { 31 | return this.date + ‘ ‘ + this.time; 32 | }; 33 | return concatDateTime.call(this); 34 | }; 35 | ``` 36 | 37 | # More Information 38 | - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind 39 | - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call 40 | - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply 41 | -------------------------------------------------------------------------------- /patterns/javascript/replace-default-param-logic-with-es6-default-params.md: -------------------------------------------------------------------------------- 1 | # Replace default parameter logic with ES6 default parameters 2 | 3 | Your using the `or` operator to provide a default value. 4 | 5 | ```javascript 6 | function getPrice(price, shippingFee) { 7 | shippingFee = shippingFee || 9.99; 8 | return price + shippingFee; 9 | } 10 | ``` 11 | 12 | You're using an `if` statement to provide a default value. 13 | 14 | ```javascript 15 | function getPrice(price, shippingFee) { 16 | if (!shippingFee) { 17 | shippingFee = 9.99; 18 | } 19 | return price + shippingFee; 20 | } 21 | ``` 22 | 23 | Use the ES6 default value syntax instead. 24 | 25 | ```javascript 26 | function getPrice(price, shippingFee = 9.99) { 27 | return price + shippingFee; 28 | } 29 | ``` 30 | 31 | # More Information 32 | 33 | - [https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/default_parameters](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/default_parameters) 34 | -------------------------------------------------------------------------------- /patterns/javascript/replace-direct-array-access-element-updates-with-map.md: -------------------------------------------------------------------------------- 1 | Replace direct array access element updates with map 2 | ===================================================== 3 | You’re iterating over an array and then performing updates on the source array directly. 4 | 5 | ```javascript 6 | var counts = [5, 7, 13]; 7 | counts.forEach(function(value, index) { 8 | counts[index]++; 9 | }); 10 | console.log(counts); 11 | ``` 12 | 13 | Use the ECMAScript 5 map method instead: 14 | 15 | ```javascript 16 | var counts = [5, 7, 13]; 17 | counts = counts.map(function(value, index) { 18 | return value + 1; 19 | }); 20 | console.log(counts); 21 | ``` 22 | 23 | # More Information 24 | 25 | - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach 26 | - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map 27 | -------------------------------------------------------------------------------- /patterns/javascript/replace-for-with-foreach.md: -------------------------------------------------------------------------------- 1 | Replace for with forEach 2 | ========================= 3 | 4 | You're using the `for` statement to iterate a collection. 5 | 6 | ```javascript 7 | var i, item, items = [1, 2, 3]; 8 | for (i = 0; i < items.length; i++) { 9 | item = items[i]; 10 | console.log(item); 11 | } 12 | ``` 13 | 14 | Use the ECMAScript 5 `forEach` method instead. 15 | 16 | ```javascript 17 | [1, 2, 3].forEach(function(item) { 18 | console.log(item); 19 | }); 20 | ``` 21 | 22 | # More Information 23 | 24 | - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for 25 | - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach 26 | -------------------------------------------------------------------------------- /patterns/javascript/replace-jquery-selector-with-query-selector.md: -------------------------------------------------------------------------------- 1 | # Replace jQuery selectors with the querySelector/querySelectorAll 2 | 3 | You're using jQuery to select elements. 4 | 5 | ```js 6 | var oneElement = $('.element-one'); 7 | var allElements = $('element'); 8 | ``` 9 | 10 | Use the Selectors API instead: 11 | 12 | ```js 13 | var oneElement = document.querySelector('.element-one'); 14 | var oneElement = document.querySelectorAll('.element'); 15 | ``` 16 | 17 | # More Information 18 | 19 | - https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector 20 | - https://developer.mozilla.org/en-US/docs/Web/API/document.querySelectorAll 21 | - https://developer.mozilla.org/en-US/docs/Web/API/element.querySelector 22 | - https://developer.mozilla.org/en-US/docs/Web/API/element.querySelectorAll 23 | -------------------------------------------------------------------------------- /patterns/javascript/replace-switch-with-object-literal.md: -------------------------------------------------------------------------------- 1 | Replace switch with object literal 2 | =================================== 3 | You’re using a switch statement to conditionally execute a fragment of code. 4 | 5 | ```javascript 6 | var factory = function(key) { 7 | var instance; 8 | switch (key) { 9 | case 'dog': 10 | instance = new Dog(); 11 | break; 12 | case 'cat': 13 | instance = new Cat(); 14 | break; 15 | default: 16 | instance = new Person(); 17 | break; 18 | } 19 | return instance; 20 | } 21 | console.log(factory('dog')); 22 | ``` 23 | 24 | Replace the switch statement with an object literal. 25 | 26 | ```javascript 27 | var factory = function(key) { 28 | var map = { 29 | dog: Dog, 30 | cat: Cat, 31 | person: Person 32 | }; 33 | return new map[map.hasOwnProperty(key) ? key : 'person'](); 34 | }; 35 | console.log(factory('dog')); 36 | ``` 37 | 38 | # More Information 39 | 40 | - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch 41 | - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Values,_variables,_and_literals#Object_literals 42 | - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty 43 | --------------------------------------------------------------------------------