├── README.md └── linters └── jshintrc /README.md: -------------------------------------------------------------------------------- 1 | # Buffer JavaScript Style Guide 2 | 3 | *A work in progress inspired by [AirBnB's Style Guide](https://github.com/airbnb/javascript).* 4 | 5 | ## Goal 6 | 7 | To establish more consistency and readability across the Buffer team and our 8 | open source projects. 9 | 10 | ## Ideals 11 | 12 | > **Have a bias toward clarity** - from [The Buffer Values](http://www.slideshare.net/Bufferapp/buffer-culture-04) 13 | 14 | - We should always aim to write code that is clear and readable. 15 | - Use whitespace. Add comments liberally where needed, but strive to write code that's clear and self documenting 16 | - Always try to write code that clearly demonstrates and communicates it's intent. 17 | 18 | ## Table of Contents 19 | 20 | 1. [Naming Conventions](#naming-conventions) 21 | 2. [Variables](#variables) 22 | 3. [Strings](#strings) 23 | 4. [Blocks](#blocks) 24 | 5. [Whitespace](#whitespace) 25 | 6. [jQuery](#jquery) 26 | 7. [Scope & this](#scope--this) 27 | 28 | *Future sections may include: Commas+Semicolons, Comments, 29 | Conditional Expressions, Variables, Properties* 30 | 31 | ## Naming Conventions 32 | 33 | - Use camelCase variables and function names. use CAPS_UNDERSCORE for constants. 34 | 35 | ```javascript 36 | var updateText = 'Point Break is the best movie ever.'; 37 | 38 | function setUpdateText(arg) { //... 39 | 40 | // Constants 41 | var MAX_TWEET_LENGTH = 140; 42 | ``` 43 | 44 | - Use PascalCase or CapFirst when creating a constructor or extending a 45 | Backbone class. Avoid using a capital first letter unless it's one of these. 46 | 47 | ```javascript 48 | function Profile(service){ 49 | this.service = service; 50 | } 51 | 52 | var myProfile = new Profile('facebook'); 53 | 54 | var ProfileView = Backbone.View.extend({ 55 | events: {} 56 | }); 57 | ``` 58 | 59 | - Use a leading _ to denote private methods 60 | 61 | ```javascript 62 | MyClass.prototype._privateMethod = function(){ //... 63 | ``` 64 | 65 | - Be descriptive in the variables purpose. 66 | 67 | ```javascript 68 | var isFacebook = profile.get('service') === 'facebook'; 69 | // over... 70 | var facebook = profile.get('service') === 'facebook'; 71 | ``` 72 | 73 | ## Variables 74 | 75 | - Remember to use `var` to declare your variables and avoid poluting the global namespace. 76 | - Use one `var` declaration per line. Avoid re-use of a single `var` declaration. 77 | This helps keep our commits simple when adding removing a variable since we won't have 78 | to edit spacing or commas/semi-colons. 79 | 80 | ```javascript 81 | var open = false; 82 | var count = someCollection.length; 83 | ``` 84 | 85 | - Declare unassigned variables last. This helps keep organized and shows what will be assigned 86 | throughout a function. 87 | 88 | ```javascript 89 | // good :) 90 | var input = document.querySelectorAll('.js-my-input'); 91 | var text = input.value; 92 | var isOverLimit; 93 | 94 | if (profile.get('service') === 'twitter') { 95 | isOverLimit = text > 140; 96 | } 97 | 98 | if (isOverLimit) { 99 | // ... 100 | } 101 | 102 | // ...not great :( 103 | if (profile.get('service') === 'twitter') { 104 | var isOverLimit = text > 140; 105 | } 106 | 107 | // variable may be undefined here 108 | if (isOverLimit) { 109 | // ... 110 | } 111 | ``` 112 | 113 | - Assign variables at the top of functions. This avoids issues with hoisting and variable declaration 114 | 115 | ```javascript 116 | function myFunc($container) { 117 | var someBoolean = false; 118 | var isOpen; 119 | 120 | // ...other logic 121 | 122 | if (someBoolean === true) { 123 | isOpen = $container.hasClass('.open'); 124 | } 125 | 126 | // ...some more logic 127 | } 128 | ``` 129 | 130 | 131 | ## Strings 132 | 133 | - Use single quotes `''` over double quotes for strings. 134 | 135 | ```javascript 136 | var update = 'Some text'; 137 | ``` 138 | 139 | - Strings longer than 80 characters should be written across multiple lines 140 | using string concatenation. Use `+` for concatenation, avoid escaping EOLs 141 | with `\`. 142 | 143 | ```javascript 144 | var update = 'Grounds, half and half, affogato sit medium, decaffeinated ' + 145 | 'cortado, acerbic whipped grinder cultivar aftertaste. Sugar, wings ' + 146 | 'robusta barista, seasonal robust, mazagran qui blue mountain organic ' + 147 | 'breve arabica.'; 148 | 149 | // or use Array#join 150 | var update = [ 151 | 'Grounds, half and half, affogato sit medium, decaffeinated ', 152 | 'cortado, acerbic whipped grinder cultivar aftertaste. Sugar, wings ', 153 | 'robusta barista, seasonal robust, mazagran qui blue mountain organic ', 154 | 'breve arabica.' 155 | ].join(''); 156 | ``` 157 | 158 | ## Blocks 159 | 160 | - Use curlys `{}` for multi-line blocks, add spacing around statements to 161 | encourage readability. 162 | 163 | ```javascript 164 | if ( update.get('text').length < 140 ) { 165 | update.save(); 166 | } else { 167 | $('.js-warning').show(); 168 | } 169 | 170 | // It's ok to drop the braces if it's a short one-liner, try to keep it 171 | // under ~80 characters long so it's readable 172 | if ( update.get('text') > 140 ) this.showLengthWarning(); 173 | 174 | ``` 175 | 176 | ## Whitespace 177 | 178 | - Use soft tabs set to **2** spaces. 179 | 180 | ```javascript 181 | function isAwesome() { 182 | ∙∙return this.plan === 'awesome'; 183 | } 184 | this.getDataForModel(123) 185 | ∙∙.then(this.renderTemplate); 186 | 187 | // instead of 188 | function isBusiness() { 189 | ∙∙∙∙return ['small', 'business', 'agency'].indexOf(this.plan) > -1; 190 | } 191 | this.getDataForModel(456) 192 | .then(this.renderTemplate); 193 | ``` 194 | 195 | - Use whitespace with operators 196 | 197 | ```javascript 198 | var count = x + 2; 199 | // instead of 200 | var count=x+2; 201 | ``` 202 | 203 | - Use indentation for longer method chaining 204 | 205 | ```javascript 206 | $update 207 | .text(newText) 208 | .removeClass('editing') 209 | .addClass('saved'); 210 | 211 | // Instead of 212 | $update.text(newText).removeClass('editing').addClass('saved'); 213 | ``` 214 | 215 | ## jQuery 216 | 217 | - Use `.js-` prefixed class selectors. This prevents confusion between 218 | classes needed for design and ones used to reference the DOM from js. 219 | 220 | ```html 221 |
Some update text
222 | ``` 223 | 224 | ```javascript 225 | var $content = $('.js-update-content'); 226 | $content.text('New text'); 227 | ``` 228 | 229 | - Prefix jQuery object variables with a `$`. This makes it easier to distingush jQuery variables vs other variables and DOM elements. 230 | 231 | ```javascript 232 | var $sidebar = $('.js-sidebar'); 233 | ``` 234 | 235 | - Cache jQuery lookups. 236 | 237 | ```javascript 238 | // Use this... 239 | var $sidebar = $('.js-sidebar'); 240 | $sidebar.addClass('hidden'); 241 | $sidebar.data('id', '1234'); 242 | 243 | // Instead of 244 | $('.js-sidebar').addClass('hidden'); 245 | $('.js-sidebar').data('id', '1234'); 246 | ``` 247 | 248 | - Use Promises with $.ajax. jQuery's docs use them as the new default. Use `.then` or `.always` over `.done` and `.fail` 249 | 250 | ```javascript 251 | $.ajax({ url: '/updates.json' }) 252 | .then(function(data){ 253 | renderUpdates(data.updates); 254 | }, function(){ 255 | alert('Request failed'); 256 | }); 257 | 258 | // Promises also work nicely for wrapping a more complex $.ajax request 259 | function saveUpdateText(updateId, text) { 260 | return $.ajax({ 261 | type: 'post'. 262 | url: '/updates/' + updateId + '.json', 263 | data: { 264 | body: text 265 | } 266 | }); 267 | } 268 | 269 | // The response 270 | function showSuccessNotification(data) { 271 | new Notification(data.message); 272 | } 273 | 274 | // Usage is very clean and easy to read 275 | saveUpdateText(123, 'My new update text') 276 | .then(showSuccessNotification); 277 | 278 | ``` 279 | 280 | ## Scope & this 281 | 282 | - Use `.bind(this)` to pass scope to functions if possible. 283 | Use `var _this = this;` if it makes your code simpler and more readable. 284 | 285 | ```javascript 286 | someMethod: function(data) { 287 | this.saveMyUpdate(data) 288 | .then(function(data){ 289 | this.collection.add(data); 290 | this.showSavedNotification(); 291 | }.bind(this)); 292 | } 293 | 294 | var myUpdates = updates.filter(function(update){ 295 | return update.type === this.getCurrentType(); 296 | }.bind(this)); 297 | 298 | // Using _this - Maybe this isn't the best example, but you get the idea :) 299 | loadModelDataInForm: function(id){ 300 | var _this = this; 301 | $.ajax({ url: '/model/' + id + '/' }) 302 | .then(function(data){ 303 | _this.renderTemplate(data); 304 | $('.js-save-button').on('click', function(e){ 305 | $(this).text('Saving...'); 306 | _this 307 | .getDataFromForm() 308 | .save(); 309 | }); 310 | }, function(err){ 311 | alert(err); 312 | }); 313 | } 314 | ``` 315 | -------------------------------------------------------------------------------- /linters/jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | /** 3 | * More info: http://www.jshint.com 4 | */ 5 | 6 | // Define normal browser globals like window and document 7 | "browser": true, 8 | 9 | // Define node globals like require + module 10 | "node": true, 11 | 12 | // Allows debug code using console.log etc. 13 | "devel": true, 14 | 15 | // Make curly braces optional 16 | "curly": false, 17 | 18 | // Allows '== null' 19 | "eqnull": true, 20 | 21 | // Allows == or != when not comparing to 0 or '' 22 | "eqeqeq": false, 23 | 24 | // Warn if using undefined variables 25 | "undef": true, 26 | 27 | // Per-project globals 28 | "globals": {}, 29 | 30 | // Disabled warnings: 31 | 32 | // Ignore missing semi-colons 33 | "-W033": true, 34 | 35 | // Ignore use dot-notation warning 36 | "-W069": true, 37 | 38 | // Ignore Mixed spaces and tabs 39 | "-W099": true, 40 | 41 | // Ignore returning assignments as statements 42 | "-W093": true, 43 | 44 | // Ignore expressions instead of assignments, i.e. using ? w/out assignment 45 | "-W030": true 46 | } 47 | --------------------------------------------------------------------------------