├── 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 = '
';
242 |
243 | for (i = 0; i < length; i++) {
244 | items += '- ' + messages[i].message + '
';
245 | }
246 |
247 | return 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 |
--------------------------------------------------------------------------------