├── gyfnice.txt ├── linters └── SublimeLinter │ └── SublimeLinter.sublime-settings └── README.md /gyfnice.txt: -------------------------------------------------------------------------------- 1 | I am gyfnice -------------------------------------------------------------------------------- /linters/SublimeLinter/SublimeLinter.sublime-settings: -------------------------------------------------------------------------------- 1 | /** 2 | * Airbnb JSHint settings for use with SublimeLinter and Sublime Text 2. 3 | * 4 | * 1. Install SublimeLinter at https://github.com/SublimeLinter/SublimeLinter 5 | * 2. Open user preferences for the SublimeLinter package in Sublime Text 2 6 | * * For Mac OS X go to _Sublime Text 2_ > _Preferences_ > _Package Settings_ > _SublimeLinter_ > _Settings - User_ 7 | * 3. Paste the contents of this file into your settings file 8 | * 4. Save the settings file 9 | * 10 | * @version 0.3.0 11 | * @see https://github.com/SublimeLinter/SublimeLinter 12 | * @see http://www.jshint.com/docs/ 13 | */ 14 | { 15 | "jshint_options": 16 | { 17 | /* 18 | * ENVIRONMENTS 19 | * ================= 20 | */ 21 | 22 | // Define globals exposed by modern browsers. 23 | "browser": true, 24 | 25 | // Define globals exposed by jQuery. 26 | "jquery": true, 27 | 28 | // Define globals exposed by Node.js. 29 | "node": true, 30 | 31 | /* 32 | * ENFORCING OPTIONS 33 | * ================= 34 | */ 35 | 36 | // Force all variable names to use either camelCase style or UPPER_CASE 37 | // with underscores. 38 | "camelcase": true, 39 | 40 | // Prohibit use of == and != in favor of === and !==. 41 | "eqeqeq": true, 42 | 43 | // Suppress warnings about == null comparisons. 44 | "eqnull": true, 45 | 46 | // Enforce tab width of 2 spaces. 47 | "indent": 2, 48 | 49 | // Prohibit use of a variable before it is defined. 50 | "latedef": true, 51 | 52 | // Require capitalized names for constructor functions. 53 | "newcap": true, 54 | 55 | // Enforce use of single quotation marks for strings. 56 | "quotmark": "single", 57 | 58 | // Prohibit trailing whitespace. 59 | "trailing": true, 60 | 61 | // Prohibit use of explicitly undeclared variables. 62 | "undef": true, 63 | 64 | // Warn when variables are defined but never used. 65 | "unused": true, 66 | 67 | // Enforce line length to 80 characters 68 | "maxlen": 80, 69 | 70 | // Enforce placing 'use strict' at the top function scope 71 | "strict": true 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Airbnb JavaScript Style Guide() { 2 | 3 | *A mostly reasonable approach to JavaScript* 4 | 5 | 6 | ## Table of Contents 7 | 8 | 1. [Types](#types) 9 | 1. [Objects](#objects) 10 | 1. [Arrays](#arrays) 11 | 1. [Strings](#strings) 12 | 1. [Functions](#functions) 13 | 1. [Properties](#properties) 14 | 1. [Variables](#variables) 15 | 1. [Hoisting](#hoisting) 16 | 1. [Conditional Expressions & Equality](#conditionals) 17 | 1. [Blocks](#blocks) 18 | 1. [Comments](#comments) 19 | 1. [Whitespace](#whitespace) 20 | 1. [Commas](#commas) 21 | 1. [Semicolons](#semicolons) 22 | 1. [Type Casting & Coercion](#type-coercion) 23 | 1. [Naming Conventions](#naming-conventions) 24 | 1. [Accessors](#accessors) 25 | 1. [Constructors](#constructors) 26 | 1. [Events](#events) 27 | 1. [Modules](#modules) 28 | 1. [jQuery](#jquery) 29 | 1. [ES5 Compatibility](#es5) 30 | 1. [Testing](#testing) 31 | 1. [Performance](#performance) 32 | 1. [Resources](#resources) 33 | 1. [In the Wild](#in-the-wild) 34 | 1. [Translation](#translation) 35 | 1. [The JavaScript Style Guide Guide](#guide-guide) 36 | 1. [Contributors](#contributors) 37 | 1. [License](#license) 38 | 39 | ## Types 40 | 41 | - **Primitives**: When you access a primitive type you work directly on its value 42 | 43 | + `string` 44 | + `number` 45 | + `boolean` 46 | + `null` 47 | + `undefined` 48 | 49 | ```javascript 50 | var foo = 1, 51 | bar = foo; 52 | 53 | bar = 9; 54 | 55 | console.log(foo, bar); // => 1, 9 56 | ``` 57 | - **Complex**: When you access a complex type you work on a reference to its value 58 | 59 | + `object` 60 | + `array` 61 | + `function` 62 | 63 | ```javascript 64 | var foo = [1, 2], 65 | bar = foo; 66 | 67 | bar[0] = 9; 68 | 69 | console.log(foo[0], bar[0]); // => 9, 9 70 | ``` 71 | 72 | **[[⬆]](#TOC)** 73 | 74 | ## Objects 75 | 76 | - Use the literal syntax for object creation. 77 | 78 | ```javascript 79 | // bad 80 | var item = new Object(); 81 | 82 | // good 83 | var item = {}; 84 | ``` 85 | 86 | - Don't use [reserved words](http://es5.github.io/#x7.6.1) as keys. It won't work in IE8. [More info](https://github.com/airbnb/javascript/issues/61) 87 | 88 | ```javascript 89 | // bad 90 | var superman = { 91 | default: { clark: 'kent' }, 92 | private: true 93 | }; 94 | 95 | // good 96 | var superman = { 97 | defaults: { clark: 'kent' }, 98 | hidden: true 99 | }; 100 | ``` 101 | 102 | - Use readable synonyms in place of reserved words. 103 | 104 | ```javascript 105 | // bad 106 | var superman = { 107 | class: 'alien' 108 | }; 109 | 110 | // bad 111 | var superman = { 112 | klass: 'alien' 113 | }; 114 | 115 | // good 116 | var superman = { 117 | type: 'alien' 118 | }; 119 | ``` 120 | **[[⬆]](#TOC)** 121 | 122 | ## Arrays 123 | 124 | - Use the literal syntax for array creation 125 | 126 | ```javascript 127 | // bad 128 | var items = new Array(); 129 | 130 | // good 131 | var items = []; 132 | ``` 133 | 134 | - If you don't know array length use Array#push. 135 | 136 | ```javascript 137 | var someStack = []; 138 | 139 | 140 | // bad 141 | someStack[someStack.length] = 'abracadabra'; 142 | 143 | // good 144 | someStack.push('abracadabra'); 145 | ``` 146 | 147 | - When you need to copy an array use Array#slice. [jsPerf](http://jsperf.com/converting-arguments-to-an-array/7) 148 | 149 | ```javascript 150 | var len = items.length, 151 | itemsCopy = [], 152 | i; 153 | 154 | // bad 155 | for (i = 0; i < len; i++) { 156 | itemsCopy[i] = items[i]; 157 | } 158 | 159 | // good 160 | itemsCopy = items.slice(); 161 | ``` 162 | 163 | - To convert an array-like object to an array, use Array#slice. 164 | 165 | ```javascript 166 | function trigger() { 167 | var args = Array.prototype.slice.call(arguments); 168 | ... 169 | } 170 | ``` 171 | 172 | **[[⬆]](#TOC)** 173 | 174 | 175 | ## Strings 176 | 177 | - Use single quotes `''` for strings 178 | 179 | ```javascript 180 | // bad 181 | var name = "Bob Parr"; 182 | 183 | // good 184 | var name = 'Bob Parr'; 185 | 186 | // bad 187 | var fullName = "Bob " + this.lastName; 188 | 189 | // good 190 | var fullName = 'Bob ' + this.lastName; 191 | ``` 192 | 193 | - Strings longer than 80 characters should be written across multiple lines using string concatenation. 194 | - Note: If overused, long strings with concatenation could impact performance. [jsPerf](http://jsperf.com/ya-string-concat) & [Discussion](https://github.com/airbnb/javascript/issues/40) 195 | 196 | ```javascript 197 | // bad 198 | var errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.'; 199 | 200 | // bad 201 | var errorMessage = 'This is a super long error that \ 202 | was thrown because of Batman. \ 203 | When you stop to think about \ 204 | how Batman had anything to do \ 205 | with this, you would get nowhere \ 206 | fast.'; 207 | 208 | 209 | // good 210 | var errorMessage = 'This is a super long error that ' + 211 | 'was thrown because of Batman. ' + 212 | 'When you stop to think about ' + 213 | 'how Batman had anything to do ' + 214 | 'with this, you would get nowhere ' + 215 | 'fast.'; 216 | ``` 217 | 218 | - When programatically building up a string, use Array#join instead of string concatenation. Mostly for IE: [jsPerf](http://jsperf.com/string-vs-array-concat/2). 219 | 220 | ```javascript 221 | var items, 222 | messages, 223 | length, 224 | i; 225 | 226 | messages = [{ 227 | state: 'success', 228 | message: 'This one worked.' 229 | }, { 230 | state: 'success', 231 | message: 'This one worked as well.' 232 | }, { 233 | state: 'error', 234 | message: 'This one did not work.' 235 | }]; 236 | 237 | length = messages.length; 238 | 239 | // bad 240 | function inbox(messages) { 241 | items = ''; 248 | } 249 | 250 | // good 251 | function inbox(messages) { 252 | items = []; 253 | 254 | for (i = 0; i < length; i++) { 255 | items[i] = messages[i].message; 256 | } 257 | 258 | return ''; 259 | } 260 | ``` 261 | 262 | **[[⬆]](#TOC)** 263 | 264 | 265 | ## Functions 266 | 267 | - Function expressions: 268 | 269 | ```javascript 270 | // anonymous function expression 271 | var anonymous = function() { 272 | return true; 273 | }; 274 | 275 | // named function expression 276 | var named = function named() { 277 | return true; 278 | }; 279 | 280 | // immediately-invoked function expression (IIFE) 281 | (function() { 282 | console.log('Welcome to the Internet. Please follow me.'); 283 | })(); 284 | ``` 285 | 286 | - Never declare a function in a non-function block (if, while, etc). Assign the function to a variable instead. Browsers will allow you to do it, but they all interpret it differently, which is bad news bears. 287 | - **Note:** ECMA-262 defines a `block` as a list of statements. A function declaration is not a statement. [Read ECMA-262's note on this issue](http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf#page=97). 288 | 289 | ```javascript 290 | // bad 291 | if (currentUser) { 292 | function test() { 293 | console.log('Nope.'); 294 | } 295 | } 296 | 297 | // good 298 | var test; 299 | if (currentUser) { 300 | test = function test() { 301 | console.log('Yup.'); 302 | }; 303 | } 304 | ``` 305 | 306 | - Never name a parameter `arguments`, this will take precedence over the `arguments` object that is given to every function scope. 307 | 308 | ```javascript 309 | // bad 310 | function nope(name, options, arguments) { 311 | // ...stuff... 312 | } 313 | 314 | // good 315 | function yup(name, options, args) { 316 | // ...stuff... 317 | } 318 | ``` 319 | 320 | **[[⬆]](#TOC)** 321 | 322 | 323 | 324 | ## Properties 325 | 326 | - Use dot notation when accessing properties. 327 | 328 | ```javascript 329 | var luke = { 330 | jedi: true, 331 | age: 28 332 | }; 333 | 334 | // bad 335 | var isJedi = luke['jedi']; 336 | 337 | // good 338 | var isJedi = luke.jedi; 339 | ``` 340 | 341 | - Use subscript notation `[]` when accessing properties with a variable. 342 | 343 | ```javascript 344 | var luke = { 345 | jedi: true, 346 | age: 28 347 | }; 348 | 349 | function getProp(prop) { 350 | return luke[prop]; 351 | } 352 | 353 | var isJedi = getProp('jedi'); 354 | ``` 355 | 356 | **[[⬆]](#TOC)** 357 | 358 | 359 | ## Variables 360 | 361 | - Always use `var` to declare variables. Not doing so will result in global variables. We want to avoid polluting the global namespace. Captain Planet warned us of that. 362 | 363 | ```javascript 364 | // bad 365 | superPower = new SuperPower(); 366 | 367 | // good 368 | var superPower = new SuperPower(); 369 | ``` 370 | 371 | - Use one `var` declaration for multiple variables and declare each variable on a newline. 372 | 373 | ```javascript 374 | // bad 375 | var items = getItems(); 376 | var goSportsTeam = true; 377 | var dragonball = 'z'; 378 | 379 | // good 380 | var items = getItems(), 381 | goSportsTeam = true, 382 | dragonball = 'z'; 383 | ``` 384 | 385 | - Declare unassigned variables last. This is helpful when later on you might need to assign a variable depending on one of the previous assigned variables. 386 | 387 | ```javascript 388 | // bad 389 | var i, len, dragonball, 390 | items = getItems(), 391 | goSportsTeam = true; 392 | 393 | // bad 394 | var i, items = getItems(), 395 | dragonball, 396 | goSportsTeam = true, 397 | len; 398 | 399 | // good 400 | var items = getItems(), 401 | goSportsTeam = true, 402 | dragonball, 403 | length, 404 | i; 405 | ``` 406 | 407 | - Assign variables at the top of their scope. This helps avoid issues with variable declaration and assignment hoisting related issues. 408 | 409 | ```javascript 410 | // bad 411 | function() { 412 | test(); 413 | console.log('doing stuff..'); 414 | 415 | //..other stuff.. 416 | 417 | var name = getName(); 418 | 419 | if (name === 'test') { 420 | return false; 421 | } 422 | 423 | return name; 424 | } 425 | 426 | // good 427 | function() { 428 | var name = getName(); 429 | 430 | test(); 431 | console.log('doing stuff..'); 432 | 433 | //..other stuff.. 434 | 435 | if (name === 'test') { 436 | return false; 437 | } 438 | 439 | return name; 440 | } 441 | 442 | // bad 443 | function() { 444 | var name = getName(); 445 | 446 | if (!arguments.length) { 447 | return false; 448 | } 449 | 450 | return true; 451 | } 452 | 453 | // good 454 | function() { 455 | if (!arguments.length) { 456 | return false; 457 | } 458 | 459 | var name = getName(); 460 | 461 | return true; 462 | } 463 | ``` 464 | 465 | **[[⬆]](#TOC)** 466 | 467 | 468 | ## Hoisting 469 | 470 | - Variable declarations get hoisted to the top of their scope, their assignment does not. 471 | 472 | ```javascript 473 | // we know this wouldn't work (assuming there 474 | // is no notDefined global variable) 475 | function example() { 476 | console.log(notDefined); // => throws a ReferenceError 477 | } 478 | 479 | // creating a variable declaration after you 480 | // reference the variable will work due to 481 | // variable hoisting. Note: the assignment 482 | // value of `true` is not hoisted. 483 | function example() { 484 | console.log(declaredButNotAssigned); // => undefined 485 | var declaredButNotAssigned = true; 486 | } 487 | 488 | // The interpreter is hoisting the variable 489 | // declaration to the top of the scope. 490 | // Which means our example could be rewritten as: 491 | function example() { 492 | var declaredButNotAssigned; 493 | console.log(declaredButNotAssigned); // => undefined 494 | declaredButNotAssigned = true; 495 | } 496 | ``` 497 | 498 | - Anonymous function expressions hoist their variable name, but not the function assignment. 499 | 500 | ```javascript 501 | function example() { 502 | console.log(anonymous); // => undefined 503 | 504 | anonymous(); // => TypeError anonymous is not a function 505 | 506 | var anonymous = function() { 507 | console.log('anonymous function expression'); 508 | }; 509 | } 510 | ``` 511 | 512 | - Named function expressions hoist the variable name, not the function name or the function body. 513 | 514 | ```javascript 515 | function example() { 516 | console.log(named); // => undefined 517 | 518 | named(); // => TypeError named is not a function 519 | 520 | superPower(); // => ReferenceError superPower is not defined 521 | 522 | var named = function superPower() { 523 | console.log('Flying'); 524 | }; 525 | } 526 | 527 | // the same is true when the function name 528 | // is the same as the variable name. 529 | function example() { 530 | console.log(named); // => undefined 531 | 532 | named(); // => TypeError named is not a function 533 | 534 | var named = function named() { 535 | console.log('named'); 536 | } 537 | } 538 | ``` 539 | 540 | - Function declarations hoist their name and the function body. 541 | 542 | ```javascript 543 | function example() { 544 | superPower(); // => Flying 545 | 546 | function superPower() { 547 | console.log('Flying'); 548 | } 549 | } 550 | ``` 551 | 552 | - For more information refer to [JavaScript Scoping & Hoisting](http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting) by [Ben Cherry](http://www.adequatelygood.com/) 553 | 554 | **[[⬆]](#TOC)** 555 | 556 | 557 | 558 | ## Conditional Expressions & Equality 559 | 560 | - Use `===` and `!==` over `==` and `!=`. 561 | - Conditional expressions are evaluated using coercion with the `ToBoolean` method and always follow these simple rules: 562 | 563 | + **Objects** evaluate to **true** 564 | + **Undefined** evaluates to **false** 565 | + **Null** evaluates to **false** 566 | + **Booleans** evaluate to **the value of the boolean** 567 | + **Numbers** evaluate to **false** if **+0, -0, or NaN**, otherwise **true** 568 | + **Strings** evaluate to **false** if an empty string `''`, otherwise **true** 569 | 570 | ```javascript 571 | if ([0]) { 572 | // true 573 | // An array is an object, objects evaluate to true 574 | } 575 | ``` 576 | 577 | - Use shortcuts. 578 | 579 | ```javascript 580 | // bad 581 | if (name !== '') { 582 | // ...stuff... 583 | } 584 | 585 | // good 586 | if (name) { 587 | // ...stuff... 588 | } 589 | 590 | // bad 591 | if (collection.length > 0) { 592 | // ...stuff... 593 | } 594 | 595 | // good 596 | if (collection.length) { 597 | // ...stuff... 598 | } 599 | ``` 600 | 601 | - For more information see [Truth Equality and JavaScript](http://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/#more-2108) by Angus Croll 602 | 603 | **[[⬆]](#TOC)** 604 | 605 | 606 | ## Blocks 607 | 608 | - Use braces with all multi-line blocks. 609 | 610 | ```javascript 611 | // bad 612 | if (test) 613 | return false; 614 | 615 | // good 616 | if (test) return false; 617 | 618 | // good 619 | if (test) { 620 | return false; 621 | } 622 | 623 | // bad 624 | function() { return false; } 625 | 626 | // good 627 | function() { 628 | return false; 629 | } 630 | ``` 631 | 632 | **[[⬆]](#TOC)** 633 | 634 | 635 | ## Comments 636 | 637 | - Use `/** ... */` for multiline comments. Include a description, specify types and values for all parameters and return values. 638 | 639 | ```javascript 640 | // bad 641 | // make() returns a new element 642 | // based on the passed in tag name 643 | // 644 | // @param tag 645 | // @return element 646 | function make(tag) { 647 | 648 | // ...stuff... 649 | 650 | return element; 651 | } 652 | 653 | // good 654 | /** 655 | * make() returns a new element 656 | * based on the passed in tag name 657 | * 658 | * @param tag 659 | * @return element 660 | */ 661 | function make(tag) { 662 | 663 | // ...stuff... 664 | 665 | return element; 666 | } 667 | ``` 668 | 669 | - Use `//` for single line comments. Place single line comments on a newline above the subject of the comment. Put an empty line before the comment. 670 | 671 | ```javascript 672 | // bad 673 | var active = true; // is current tab 674 | 675 | // good 676 | // is current tab 677 | var active = true; 678 | 679 | // bad 680 | function getType() { 681 | console.log('fetching type...'); 682 | // set the default type to 'no type' 683 | var type = this._type || 'no type'; 684 | 685 | return type; 686 | } 687 | 688 | // good 689 | function getType() { 690 | console.log('fetching type...'); 691 | 692 | // set the default type to 'no type' 693 | var type = this._type || 'no type'; 694 | 695 | return type; 696 | } 697 | ``` 698 | 699 | - Prefixing your comments with `FIXME` or `TODO` helps other developers quickly understand if you're pointing out a problem that needs to be revisited, or if you're suggesting a solution to the problem that needs to be implemented. These are different than regular comments because they are actionable. The actions are `FIXME -- need to figure this out` or `TODO -- need to implement`. 700 | 701 | - Use `// FIXME:` to annotate problems 702 | 703 | ```javascript 704 | function Calculator() { 705 | 706 | // FIXME: shouldn't use a global here 707 | total = 0; 708 | 709 | return this; 710 | } 711 | ``` 712 | 713 | - Use `// TODO:` to annotate solutions to problems 714 | 715 | ```javascript 716 | function Calculator() { 717 | 718 | // TODO: total should be configurable by an options param 719 | this.total = 0; 720 | 721 | return this; 722 | } 723 | ``` 724 | 725 | **[[⬆]](#TOC)** 726 | 727 | 728 | ## Whitespace 729 | 730 | - Use soft tabs set to 2 spaces 731 | 732 | ```javascript 733 | // bad 734 | function() { 735 | ∙∙∙∙var name; 736 | } 737 | 738 | // bad 739 | function() { 740 | ∙var name; 741 | } 742 | 743 | // good 744 | function() { 745 | ∙∙var name; 746 | } 747 | ``` 748 | 749 | - Place 1 space before the leading brace. 750 | 751 | ```javascript 752 | // bad 753 | function test(){ 754 | console.log('test'); 755 | } 756 | 757 | // good 758 | function test() { 759 | console.log('test'); 760 | } 761 | 762 | // bad 763 | dog.set('attr',{ 764 | age: '1 year', 765 | breed: 'Bernese Mountain Dog' 766 | }); 767 | 768 | // good 769 | dog.set('attr', { 770 | age: '1 year', 771 | breed: 'Bernese Mountain Dog' 772 | }); 773 | ``` 774 | 775 | - Set off operators with spaces. 776 | 777 | ```javascript 778 | // bad 779 | var x=y+5; 780 | 781 | // good 782 | var x = y + 5; 783 | ``` 784 | 785 | - Place an empty newline at the end of the file. 786 | 787 | ```javascript 788 | // bad 789 | (function(global) { 790 | // ...stuff... 791 | })(this); 792 | ``` 793 | 794 | ```javascript 795 | // good 796 | (function(global) { 797 | // ...stuff... 798 | })(this); 799 | 800 | ``` 801 | 802 | - Use indentation when making long method chains. 803 | 804 | ```javascript 805 | // bad 806 | $('#items').find('.selected').highlight().end().find('.open').updateCount(); 807 | 808 | // good 809 | $('#items') 810 | .find('.selected') 811 | .highlight() 812 | .end() 813 | .find('.open') 814 | .updateCount(); 815 | 816 | // bad 817 | var leds = stage.selectAll('.led').data(data).enter().append('svg:svg').class('led', true) 818 | .attr('width', (radius + margin) * 2).append('svg:g') 819 | .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') 820 | .call(tron.led); 821 | 822 | // good 823 | var leds = stage.selectAll('.led') 824 | .data(data) 825 | .enter().append('svg:svg') 826 | .class('led', true) 827 | .attr('width', (radius + margin) * 2) 828 | .append('svg:g') 829 | .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') 830 | .call(tron.led); 831 | ``` 832 | 833 | **[[⬆]](#TOC)** 834 | 835 | ## Commas 836 | 837 | - Leading commas: **Nope.** 838 | 839 | ```javascript 840 | // bad 841 | var once 842 | , upon 843 | , aTime; 844 | 845 | // good 846 | var once, 847 | upon, 848 | aTime; 849 | 850 | // bad 851 | var hero = { 852 | firstName: 'Bob' 853 | , lastName: 'Parr' 854 | , heroName: 'Mr. Incredible' 855 | , superPower: 'strength' 856 | }; 857 | 858 | // good 859 | var hero = { 860 | firstName: 'Bob', 861 | lastName: 'Parr', 862 | heroName: 'Mr. Incredible', 863 | superPower: 'strength' 864 | }; 865 | ``` 866 | 867 | - Additional trailing comma: **Nope.** This can cause problems with IE6/7 and IE9 if it's in quirksmode. Also, in some implementations of ES3 would add length to an array if it had an additional trailing comma. This was clarified in ES5 ([source](http://es5.github.io/#D)): 868 | 869 | > Edition 5 clarifies the fact that a trailing comma at the end of an ArrayInitialiser does not add to the length of the array. This is not a semantic change from Edition 3 but some implementations may have previously misinterpreted this. 870 | 871 | ```javascript 872 | // bad 873 | var hero = { 874 | firstName: 'Kevin', 875 | lastName: 'Flynn', 876 | }; 877 | 878 | var heroes = [ 879 | 'Batman', 880 | 'Superman', 881 | ]; 882 | 883 | // good 884 | var hero = { 885 | firstName: 'Kevin', 886 | lastName: 'Flynn' 887 | }; 888 | 889 | var heroes = [ 890 | 'Batman', 891 | 'Superman' 892 | ]; 893 | ``` 894 | 895 | **[[⬆]](#TOC)** 896 | 897 | 898 | ## Semicolons 899 | 900 | - **Yup.** 901 | 902 | ```javascript 903 | // bad 904 | (function() { 905 | var name = 'Skywalker' 906 | return name 907 | })() 908 | 909 | // good 910 | (function() { 911 | var name = 'Skywalker'; 912 | return name; 913 | })(); 914 | 915 | // good 916 | ;(function() { 917 | var name = 'Skywalker'; 918 | return name; 919 | })(); 920 | ``` 921 | 922 | **[[⬆]](#TOC)** 923 | 924 | 925 | ## Type Casting & Coercion 926 | 927 | - Perform type coercion at the beginning of the statement. 928 | - Strings: 929 | 930 | ```javascript 931 | // => this.reviewScore = 9; 932 | 933 | // bad 934 | var totalScore = this.reviewScore + ''; 935 | 936 | // good 937 | var totalScore = '' + this.reviewScore; 938 | 939 | // bad 940 | var totalScore = '' + this.reviewScore + ' total score'; 941 | 942 | // good 943 | var totalScore = this.reviewScore + ' total score'; 944 | ``` 945 | 946 | - Use `parseInt` for Numbers and always with a radix for type casting. 947 | 948 | ```javascript 949 | var inputValue = '4'; 950 | 951 | // bad 952 | var val = new Number(inputValue); 953 | 954 | // bad 955 | var val = +inputValue; 956 | 957 | // bad 958 | var val = inputValue >> 0; 959 | 960 | // bad 961 | var val = parseInt(inputValue); 962 | 963 | // good 964 | var val = Number(inputValue); 965 | 966 | // good 967 | var val = parseInt(inputValue, 10); 968 | ``` 969 | 970 | - If for whatever reason you are doing something wild and `parseInt` is your bottleneck and need to use Bitshift for [performance reasons](http://jsperf.com/coercion-vs-casting/3), leave a comment explaining why and what you're doing. 971 | - **Note:** Be careful when using bitshift operations. Numbers are represented as [64-bit values](http://es5.github.io/#x4.3.19), but Bitshift operations always return a 32-bit integer ([source](http://es5.github.io/#x11.7)). Bitshift can lead to unexpected behavior for integer values larger than 32 bits. [Discussion](https://github.com/airbnb/javascript/issues/109) 972 | 973 | ```javascript 974 | // good 975 | /** 976 | * parseInt was the reason my code was slow. 977 | * Bitshifting the String to coerce it to a 978 | * Number made it a lot faster. 979 | */ 980 | var val = inputValue >> 0; 981 | ``` 982 | 983 | - Booleans: 984 | 985 | ```javascript 986 | var age = 0; 987 | 988 | // bad 989 | var hasAge = new Boolean(age); 990 | 991 | // good 992 | var hasAge = Boolean(age); 993 | 994 | // good 995 | var hasAge = !!age; 996 | ``` 997 | 998 | **[[⬆]](#TOC)** 999 | 1000 | 1001 | ## Naming Conventions 1002 | 1003 | - Avoid single letter names. Be descriptive with your naming. 1004 | 1005 | ```javascript 1006 | // bad 1007 | function q() { 1008 | // ...stuff... 1009 | } 1010 | 1011 | // good 1012 | function query() { 1013 | // ..stuff.. 1014 | } 1015 | ``` 1016 | 1017 | - Use camelCase when naming objects, functions, and instances 1018 | 1019 | ```javascript 1020 | // bad 1021 | var OBJEcttsssss = {}; 1022 | var this_is_my_object = {}; 1023 | function c() {}; 1024 | var u = new user({ 1025 | name: 'Bob Parr' 1026 | }); 1027 | 1028 | // good 1029 | var thisIsMyObject = {}; 1030 | function thisIsMyFunction() {}; 1031 | var user = new User({ 1032 | name: 'Bob Parr' 1033 | }); 1034 | ``` 1035 | 1036 | - Use PascalCase when naming constructors or classes 1037 | 1038 | ```javascript 1039 | // bad 1040 | function user(options) { 1041 | this.name = options.name; 1042 | } 1043 | 1044 | var bad = new user({ 1045 | name: 'nope' 1046 | }); 1047 | 1048 | // good 1049 | function User(options) { 1050 | this.name = options.name; 1051 | } 1052 | 1053 | var good = new User({ 1054 | name: 'yup' 1055 | }); 1056 | ``` 1057 | 1058 | - Use a leading underscore `_` when naming private properties 1059 | 1060 | ```javascript 1061 | // bad 1062 | this.__firstName__ = 'Panda'; 1063 | this.firstName_ = 'Panda'; 1064 | 1065 | // good 1066 | this._firstName = 'Panda'; 1067 | ``` 1068 | 1069 | - When saving a reference to `this` use `_this`. 1070 | 1071 | ```javascript 1072 | // bad 1073 | function() { 1074 | var self = this; 1075 | return function() { 1076 | console.log(self); 1077 | }; 1078 | } 1079 | 1080 | // bad 1081 | function() { 1082 | var that = this; 1083 | return function() { 1084 | console.log(that); 1085 | }; 1086 | } 1087 | 1088 | // good 1089 | function() { 1090 | var _this = this; 1091 | return function() { 1092 | console.log(_this); 1093 | }; 1094 | } 1095 | ``` 1096 | 1097 | - Name your functions. This is helpful for stack traces. //I don't understand why this can stack traces?? 1098 | 1099 | ```javascript 1100 | // bad I think this is good. 1101 | var log = function(msg) { 1102 | console.log(msg); 1103 | }; 1104 | 1105 | // good I can't understand why is good? I think is not necessary js style.I hope you can explain this problem for me, I really confused. thank you. 1106 | var log = function log(msg) { 1107 | console.log(msg); 1108 | }; 1109 | ``` 1110 | 1111 | **[[⬆]](#TOC)** 1112 | 1113 | 1114 | ## Accessors 1115 | 1116 | - Accessor functions for properties are not required 1117 | - If you do make accessor functions use getVal() and setVal('hello') 1118 | 1119 | ```javascript 1120 | // bad 1121 | dragon.age(); 1122 | 1123 | // good 1124 | dragon.getAge(); 1125 | 1126 | // bad 1127 | dragon.age(25); 1128 | 1129 | // good 1130 | dragon.setAge(25); 1131 | ``` 1132 | 1133 | - If the property is a boolean, use isVal() or hasVal() 1134 | 1135 | ```javascript 1136 | // bad 1137 | if (!dragon.age()) { 1138 | return false; 1139 | } 1140 | 1141 | // good 1142 | if (!dragon.hasAge()) { 1143 | return false; 1144 | } 1145 | ``` 1146 | 1147 | - It's okay to create get() and set() functions, but be consistent. 1148 | 1149 | ```javascript 1150 | function Jedi(options) { 1151 | options || (options = {}); 1152 | var lightsaber = options.lightsaber || 'blue'; 1153 | this.set('lightsaber', lightsaber); 1154 | } 1155 | 1156 | Jedi.prototype.set = function(key, val) { 1157 | this[key] = val; 1158 | }; 1159 | 1160 | Jedi.prototype.get = function(key) { 1161 | return this[key]; 1162 | }; 1163 | ``` 1164 | 1165 | **[[⬆]](#TOC)** 1166 | 1167 | 1168 | ## Constructors 1169 | 1170 | - Assign methods to the prototype object, instead of overwriting the prototype with a new object. Overwriting the prototype makes inheritance impossible: by resetting the prototype you'll overwrite the base! 1171 | 1172 | ```javascript 1173 | function Jedi() { 1174 | console.log('new jedi'); 1175 | } 1176 | 1177 | // bad 1178 | Jedi.prototype = { 1179 | fight: function fight() { 1180 | console.log('fighting'); 1181 | }, 1182 | 1183 | block: function block() { 1184 | console.log('blocking'); 1185 | } 1186 | }; 1187 | 1188 | // good 1189 | Jedi.prototype.fight = function fight() { 1190 | console.log('fighting'); 1191 | }; 1192 | 1193 | Jedi.prototype.block = function block() { 1194 | console.log('blocking'); 1195 | }; 1196 | ``` 1197 | 1198 | - Methods can return `this` to help with method chaining. 1199 | 1200 | ```javascript 1201 | // bad 1202 | Jedi.prototype.jump = function() { 1203 | this.jumping = true; 1204 | return true; 1205 | }; 1206 | 1207 | Jedi.prototype.setHeight = function(height) { 1208 | this.height = height; 1209 | }; 1210 | 1211 | var luke = new Jedi(); 1212 | luke.jump(); // => true 1213 | luke.setHeight(20) // => undefined 1214 | 1215 | // good 1216 | Jedi.prototype.jump = function() { 1217 | this.jumping = true; 1218 | return this; 1219 | }; 1220 | 1221 | Jedi.prototype.setHeight = function(height) { 1222 | this.height = height; 1223 | return this; 1224 | }; 1225 | 1226 | var luke = new Jedi(); 1227 | 1228 | luke.jump() 1229 | .setHeight(20); 1230 | ``` 1231 | 1232 | 1233 | - It's okay to write a custom toString() method, just make sure it works successfully and causes no side effects. 1234 | 1235 | ```javascript 1236 | function Jedi(options) { 1237 | options || (options = {}); 1238 | this.name = options.name || 'no name'; 1239 | } 1240 | 1241 | Jedi.prototype.getName = function getName() { 1242 | return this.name; 1243 | }; 1244 | 1245 | Jedi.prototype.toString = function toString() { 1246 | return 'Jedi - ' + this.getName(); 1247 | }; 1248 | ``` 1249 | 1250 | **[[⬆]](#TOC)** 1251 | 1252 | 1253 | ## Events 1254 | 1255 | - When attaching data payloads to events (whether DOM events or something more proprietary like Backbone events), pass a hash instead of a raw value. This allows a subsequent contributor to add more data to the event payload without finding and updating every handler for the event. For example, instead of: 1256 | 1257 | ```js 1258 | // bad 1259 | $(this).trigger('listingUpdated', listing.id); 1260 | 1261 | ... 1262 | 1263 | $(this).on('listingUpdated', function(e, listingId) { 1264 | // do something with listingId 1265 | }); 1266 | ``` 1267 | 1268 | prefer: 1269 | 1270 | ```js 1271 | // good 1272 | $(this).trigger('listingUpdated', { listingId : listing.id }); 1273 | 1274 | ... 1275 | 1276 | $(this).on('listingUpdated', function(e, data) { 1277 | // do something with data.listingId 1278 | }); 1279 | ``` 1280 | 1281 | **[[⬆]](#TOC)** 1282 | 1283 | 1284 | ## Modules 1285 | 1286 | - The module should start with a `!`. This ensures that if a malformed module forgets to include a final semicolon there aren't errors in production when the scripts get concatenated. [Explanation](https://github.com/airbnb/javascript/issues/44#issuecomment-13063933) 1287 | - The file should be named with camelCase, live in a folder with the same name, and match the name of the single export. 1288 | - Add a method called noConflict() that sets the exported module to the previous version and returns this one. 1289 | - Always declare `'use strict';` at the top of the module. 1290 | 1291 | ```javascript 1292 | // fancyInput/fancyInput.js 1293 | 1294 | !function(global) { 1295 | 'use strict'; 1296 | 1297 | var previousFancyInput = global.FancyInput; 1298 | 1299 | function FancyInput(options) { 1300 | this.options = options || {}; 1301 | } 1302 | 1303 | FancyInput.noConflict = function noConflict() { 1304 | global.FancyInput = previousFancyInput; 1305 | return FancyInput; 1306 | }; 1307 | 1308 | global.FancyInput = FancyInput; 1309 | }(this); 1310 | ``` 1311 | 1312 | **[[⬆]](#TOC)** 1313 | 1314 | 1315 | ## jQuery 1316 | 1317 | - Prefix jQuery object variables with a `$`. 1318 | 1319 | ```javascript 1320 | // bad 1321 | var sidebar = $('.sidebar'); 1322 | 1323 | // good 1324 | var $sidebar = $('.sidebar'); 1325 | ``` 1326 | 1327 | - Cache jQuery lookups. 1328 | 1329 | ```javascript 1330 | // bad 1331 | function setSidebar() { 1332 | $('.sidebar').hide(); 1333 | 1334 | // ...stuff... 1335 | 1336 | $('.sidebar').css({ 1337 | 'background-color': 'pink' 1338 | }); 1339 | } 1340 | 1341 | // good 1342 | function setSidebar() { 1343 | var $sidebar = $('.sidebar'); 1344 | $sidebar.hide(); 1345 | 1346 | // ...stuff... 1347 | 1348 | $sidebar.css({ 1349 | 'background-color': 'pink' 1350 | }); 1351 | } 1352 | ``` 1353 | 1354 | - For DOM queries use Cascading `$('.sidebar ul')` or parent > child `$('.sidebar > ul')`. [jsPerf](http://jsperf.com/jquery-find-vs-context-sel/16) 1355 | - Use `find` with scoped jQuery object queries. 1356 | 1357 | ```javascript 1358 | // bad 1359 | $('ul', '.sidebar').hide(); 1360 | 1361 | // bad 1362 | $('.sidebar').find('ul').hide(); 1363 | 1364 | // good 1365 | $('.sidebar ul').hide(); 1366 | 1367 | // good 1368 | $('.sidebar > ul').hide(); 1369 | 1370 | // good 1371 | $sidebar.find('ul').hide(); 1372 | ``` 1373 | 1374 | **[[⬆]](#TOC)** 1375 | 1376 | 1377 | ## ECMAScript 5 Compatibility 1378 | 1379 | - Refer to [Kangax](https://twitter.com/kangax/)'s ES5 [compatibility table](http://kangax.github.com/es5-compat-table/) 1380 | 1381 | **[[⬆]](#TOC)** 1382 | 1383 | 1384 | ## Testing 1385 | 1386 | - **Yup.** 1387 | 1388 | ```javascript 1389 | function() { 1390 | return true; 1391 | } 1392 | ``` 1393 | 1394 | **[[⬆]](#TOC)** 1395 | 1396 | 1397 | ## Performance 1398 | 1399 | - [On Layout & Web Performance](http://kellegous.com/j/2013/01/26/layout-performance/) 1400 | - [String vs Array Concat](http://jsperf.com/string-vs-array-concat/2) 1401 | - [Try/Catch Cost In a Loop](http://jsperf.com/try-catch-in-loop-cost) 1402 | - [Bang Function](http://jsperf.com/bang-function) 1403 | - [jQuery Find vs Context, Selector](http://jsperf.com/jquery-find-vs-context-sel/13) 1404 | - [innerHTML vs textContent for script text](http://jsperf.com/innerhtml-vs-textcontent-for-script-text) 1405 | - [Long String Concatenation](http://jsperf.com/ya-string-concat) 1406 | - Loading... 1407 | 1408 | **[[⬆]](#TOC)** 1409 | 1410 | 1411 | ## Resources 1412 | 1413 | 1414 | **Read This** 1415 | 1416 | - [Annotated ECMAScript 5.1](http://es5.github.com/) 1417 | 1418 | **Other Styleguides** 1419 | 1420 | - [Google JavaScript Style Guide](http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml) 1421 | - [jQuery Core Style Guidelines](http://docs.jquery.com/JQuery_Core_Style_Guidelines) 1422 | - [Principles of Writing Consistent, Idiomatic JavaScript](https://github.com/rwldrn/idiomatic.js/) 1423 | 1424 | **Other Styles** 1425 | 1426 | - [Naming this in nested functions](https://gist.github.com/4135065) - Christian Johansen 1427 | - [Conditional Callbacks](https://github.com/airbnb/javascript/issues/52) 1428 | - [Popular JavaScript Coding Conventions on Github](http://sideeffect.kr/popularconvention/#javascript) 1429 | 1430 | **Further Reading** 1431 | 1432 | - [Understanding JavaScript Closures](http://javascriptweblog.wordpress.com/2010/10/25/understanding-javascript-closures/) - Angus Croll 1433 | - [Basic JavaScript for the impatient programmer](http://www.2ality.com/2013/06/basic-javascript.html) - Dr. Axel Rauschmayer 1434 | 1435 | **Books** 1436 | 1437 | - [JavaScript: The Good Parts](http://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742) - Douglas Crockford 1438 | - [JavaScript Patterns](http://www.amazon.com/JavaScript-Patterns-Stoyan-Stefanov/dp/0596806752) - Stoyan Stefanov 1439 | - [Pro JavaScript Design Patterns](http://www.amazon.com/JavaScript-Design-Patterns-Recipes-Problem-Solution/dp/159059908X) - Ross Harmes and Dustin Diaz 1440 | - [High Performance Web Sites: Essential Knowledge for Front-End Engineers](http://www.amazon.com/High-Performance-Web-Sites-Essential/dp/0596529309) - Steve Souders 1441 | - [Maintainable JavaScript](http://www.amazon.com/Maintainable-JavaScript-Nicholas-C-Zakas/dp/1449327680) - Nicholas C. Zakas 1442 | - [JavaScript Web Applications](http://www.amazon.com/JavaScript-Web-Applications-Alex-MacCaw/dp/144930351X) - Alex MacCaw 1443 | - [Pro JavaScript Techniques](http://www.amazon.com/Pro-JavaScript-Techniques-John-Resig/dp/1590597273) - John Resig 1444 | - [Smashing Node.js: JavaScript Everywhere](http://www.amazon.com/Smashing-Node-js-JavaScript-Everywhere-Magazine/dp/1119962595) - Guillermo Rauch 1445 | - [Secrets of the JavaScript Ninja](http://www.amazon.com/Secrets-JavaScript-Ninja-John-Resig/dp/193398869X) - John Resig and Bear Bibeault 1446 | - [Human JavaScript](http://humanjavascript.com/) - Henrik Joreteg 1447 | - [Superhero.js](http://superherojs.com/) - Kim Joar Bekkelund, Mads Mobæk, & Olav Bjorkoy 1448 | - [JSBooks](http://jsbooks.revolunet.com/) 1449 | - [Third Party JavaScript](http://manning.com/vinegar/) - Ben Vinegar and Anton Kovalyov 1450 | 1451 | **Blogs** 1452 | 1453 | - [DailyJS](http://dailyjs.com/) 1454 | - [JavaScript Weekly](http://javascriptweekly.com/) 1455 | - [JavaScript, JavaScript...](http://javascriptweblog.wordpress.com/) 1456 | - [Bocoup Weblog](http://weblog.bocoup.com/) 1457 | - [Adequately Good](http://www.adequatelygood.com/) 1458 | - [NCZOnline](http://www.nczonline.net/) 1459 | - [Perfection Kills](http://perfectionkills.com/) 1460 | - [Ben Alman](http://benalman.com/) 1461 | - [Dmitry Baranovskiy](http://dmitry.baranovskiy.com/) 1462 | - [Dustin Diaz](http://dustindiaz.com/) 1463 | - [nettuts](http://net.tutsplus.com/?s=javascript) 1464 | 1465 | **[[⬆]](#TOC)** 1466 | 1467 | ## In the Wild 1468 | 1469 | This is a list of organizations that are using this style guide. Send us a pull request or open an issue and we'll add you to the list. 1470 | 1471 | - **Aan Zee**: [AanZee/javascript](https://github.com/AanZee/javascript) 1472 | - **Airbnb**: [airbnb/javascript](https://github.com/airbnb/javascript) 1473 | - **American Insitutes for Research**: [AIRAST/javascript](https://github.com/AIRAST/javascript) 1474 | - **Compass Learning**: [compasslearning/javascript-style-guide](https://github.com/compasslearning/javascript-style-guide) 1475 | - **ExactTarget**: [ExactTarget/javascript](https://github.com/ExactTarget/javascript) 1476 | - **Gawker Media**: [gawkermedia/javascript](https://github.com/gawkermedia/javascript) 1477 | - **GeneralElectric**: [GeneralElectric/javascript](https://github.com/GeneralElectric/javascript) 1478 | - **GoodData**: [gooddata/gdc-js-style](https://github.com/gooddata/gdc-js-style) 1479 | - **Grooveshark**: [grooveshark/javascript](https://github.com/grooveshark/javascript) 1480 | - **How About We**: [howaboutwe/javascript](https://github.com/howaboutwe/javascript) 1481 | - **Mighty Spring**: [mightyspring/javascript](https://github.com/mightyspring/javascript) 1482 | - **MinnPost**: [MinnPost/javascript](https://github.com/MinnPost/javascript) 1483 | - **ModCloth**: [modcloth/javascript](https://github.com/modcloth/javascript) 1484 | - **National Geographic**: [natgeo/javascript](https://github.com/natgeo/javascript) 1485 | - **National Park Service**: [nationalparkservice/javascript](https://github.com/nationalparkservice/javascript) 1486 | - **Razorfish**: [razorfish/javascript-style-guide](https://github.com/razorfish/javascript-style-guide) 1487 | - **REI**: [reidev/js-style-guide](https://github.com/reidev/js-style-guide) 1488 | - **Shutterfly**: [shutterfly/javascript](https://github.com/shutterfly/javascript) 1489 | - **Userify**: [userify/javascript](https://github.com/userify/javascript) 1490 | - **Zillow**: [zillow/javascript](https://github.com/zillow/javascript) 1491 | - **ZocDoc**: [ZocDoc/javascript](https://github.com/ZocDoc/javascript) 1492 | 1493 | ## Translation 1494 | 1495 | This style guide is also available in other languages: 1496 | 1497 | - :de: **German**: [timofurrer/javascript-style-guide](https://github.com/timofurrer/javascript-style-guide) 1498 | - :jp: **Japanese**: [mitsuruog/javacript-style-guide](https://github.com/mitsuruog/javacript-style-guide) 1499 | - :br: **Portuguese**: [armoucar/javascript-style-guide](https://github.com/armoucar/javascript-style-guide) 1500 | - :cn: **Chinese**: [adamlu/javascript-style-guide](https://github.com/adamlu/javascript-style-guide) 1501 | - :es: **Spanish**: [paolocarrasco/javascript-style-guide](https://github.com/paolocarrasco/javascript-style-guide) 1502 | - :kr: **Korean**: [tipjs/javascript-style-guide](https://github.com/tipjs/javascript-style-guide) 1503 | - :fr: **French**: [nmussy/javascript-style-guide](https://github.com/nmussy/javascript-style-guide) 1504 | - :ru: **Russian**: [uprock/javascript](https://github.com/uprock/javascript) 1505 | - :bg: **Bulgarian**: [borislavvv/javascript](https://github.com/borislavvv/javascript) 1506 | 1507 | ## The JavaScript Style Guide Guide 1508 | 1509 | - [Reference](https://github.com/airbnb/javascript/wiki/The-JavaScript-Style-Guide-Guide) 1510 | 1511 | ## Contributors 1512 | 1513 | - [View Contributors](https://github.com/airbnb/javascript/graphs/contributors) 1514 | 1515 | 1516 | ## License 1517 | 1518 | (The MIT License) 1519 | 1520 | Copyright (c) 2012 Airbnb 1521 | 1522 | Permission is hereby granted, free of charge, to any person obtaining 1523 | a copy of this software and associated documentation files (the 1524 | 'Software'), to deal in the Software without restriction, including 1525 | without limitation the rights to use, copy, modify, merge, publish, 1526 | distribute, sublicense, and/or sell copies of the Software, and to 1527 | permit persons to whom the Software is furnished to do so, subject to 1528 | the following conditions: 1529 | 1530 | The above copyright notice and this permission notice shall be 1531 | included in all copies or substantial portions of the Software. 1532 | 1533 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 1534 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 1535 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 1536 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 1537 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 1538 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 1539 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 1540 | 1541 | **[[⬆]](#TOC)** 1542 | 1543 | # }; 1544 | 1545 | --------------------------------------------------------------------------------