| <!DOCTYPE html> |
| |
| <html> |
| <head> |
| <meta charset="utf-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1" /> |
| <link rel="stylesheet" href="../../vendors/prettify/prettify.css" /> |
| <script src="../../vendors/jquery/jquery.min.js" ></script> |
| <script src="../../vendors/prettify/prettify.js" ></script> |
| <script src="../../vendors/prettify/lang-css.js" ></script> |
| |
| <style> |
| |
| html, |
| body { |
| width: 100%; |
| height: 100%; |
| margin: 0; |
| padding: 0; |
| font-family: Arial; |
| line-height: 1.5; |
| } |
| |
| .toc { |
| position: fixed; |
| left: 0; |
| top: 0; |
| height: 100%; |
| width: 180px; |
| overflow: auto; |
| padding: 10px; |
| } |
| .toc a, |
| .toc a:visited { |
| color: #4c4f54; |
| text-decoration: none; |
| } |
| .toc h1 { |
| display: none; |
| } |
| .toc h2, |
| .toc h3, |
| .toc h4, |
| .toc h5 { |
| line-height: 2; |
| padding: 0; |
| margin: 0; |
| font-weight: normal; |
| } |
| .toc h2 { |
| font-size: 16px; |
| } |
| .toc h3, |
| .toc h4, |
| .toc h5 { |
| font-size: 14px; |
| text-indent: 10px; |
| } |
| |
| .markdown-body { |
| margin-left: 180px; |
| padding: 0 20px 20px 20px; |
| border-left: 1px solid #ccc; |
| } |
| .markdown-body p { |
| font-size: 14px; |
| } |
| .markdown-body h1 { |
| border-bottom: 1px solid #ccc; |
| padding: 20px 0; |
| margin: 0; |
| } |
| .markdown-body h1, |
| .markdown-body h2, |
| .markdown-body h3, |
| .markdown-body h4 { |
| color: #0356c3; |
| } |
| .markdown-body code { |
| border: 1px solid #ccc; |
| border-radius: 3px; |
| padding: 0 2px; |
| background: #efefef; |
| } |
| .markdown-body pre { |
| line-height: 1.2; |
| background: #f8faff; |
| padding: 15px; |
| border: 1px solid #ccc; |
| border-radius: 6px; |
| } |
| .markdown-body pre code { |
| border: none; |
| padding: 0; |
| background: none; |
| } |
| .markdown-body strong { |
| font-weight: normal; |
| color: #d43b09; |
| } |
| |
| </style> |
| </head> |
| <body> |
| <div class="toc"> |
| <h1><a href="#coding-standard-of-echarts">Coding Standard of ECharts</a></h1> |
| <h2><a href="#code-style">Code Style</a></h2> |
| <h3><a href="#file">File</a></h3> |
| <h3><a href="#indentation">Indentation</a></h3> |
| <h3><a href="#space">Space</a></h3> |
| <h3><a href="#line-break">Line Break</a></h3> |
| <h3><a href="#statement">Statement</a></h3> |
| <h3><a href="#naming-conventions">Naming Conventions</a></h3> |
| <h2><a href="#language-features">Language features</a></h2> |
| <h3><a href="#compatibility">Compatibility</a></h3> |
| <h3><a href="#variable">Variable</a></h3> |
| <h3><a href="#condition">Condition</a></h3> |
| <h3><a href="#type-conversion">Type Conversion</a></h3> |
| <h3><a href="#string-object-array">String, Object, Array</a></h3> |
| <h3><a href="#others">Others</a></h3> |
| |
| </div> |
| |
| <div class="markdown-body"> |
| <h1 id="coding-standard-of-echarts">Coding Standard of ECharts</h1> |
| <h2 id="code-style">Code Style</h2> |
| <h3 id="file">File</h3> |
| <p><strong>[MUST]</strong> JavaScript Source files must be encoded in UTF-8 without BOM.</p> |
| <h3 id="indentation">Indentation</h3> |
| <p><strong>[MUST]</strong> 4 space indentation. tabs and 2 space are not allowed.</p> |
| <p><strong>[MUST]</strong> <code>case</code> and <code>default</code> in <code>switch</code> must be indented.</p> |
| <pre><code class="lang-js">// good |
| switch (variable) { |
| case '1': |
| // do... |
| break; |
| case '2': |
| // do... |
| break; |
| default: |
| // do... |
| } |
| |
| // bad |
| switch (variable) { |
| case '1': |
| // do... |
| break; |
| case '2': |
| // do... |
| break; |
| default: |
| // do... |
| } |
| </code></pre> |
| <h3 id="space">Space</h3> |
| <p><strong>[MUST]</strong> Set off binary operator with spaces. But place no space between unary operator and its operand.</p> |
| <pre><code class="lang-js">var a = !arr.length; |
| a++; |
| a = b + c; |
| </code></pre> |
| <p><strong>[MUST]</strong> Place 1 space before the leading brace.</p> |
| <pre><code class="lang-js">// good |
| |
| if (condition) { |
| } |
| |
| set('attr', { |
| some: 'xxx', |
| any: 'yyy' |
| }); |
| |
| function funcName() { |
| } |
| |
| |
| // bad |
| |
| if (condition){ |
| } |
| |
| set('attr',{ |
| some: 'xxx', |
| any: 'yyy' |
| }); |
| |
| function funcName(){ |
| } |
| </code></pre> |
| <p><strong>[MUST]</strong> Place 1 space after <code>if</code> / <code>else</code> / <code>for</code> / <code>while</code> / <code>function</code> / <code>switch</code> / <code>do</code> / <code>try</code> / <code>catch</code> / <code>finally</code>.</p> |
| <pre><code class="lang-js">// good |
| |
| if (condition) { |
| } |
| |
| while (condition) { |
| } |
| |
| (function () { |
| })(); |
| |
| |
| // bad |
| |
| if(condition) { |
| } |
| |
| while(condition) { |
| } |
| |
| (function() { |
| })(); |
| </code></pre> |
| <p><strong>[MUST]</strong> In the object creating statement, place 1 space after <code>:</code>, but no space before it.</p> |
| <pre><code class="lang-js">// good |
| var obj = { |
| a: 1, |
| b: 2, |
| c: 3 |
| }; |
| |
| // bad |
| var obj = { |
| a : 1, |
| b:2, |
| c :3 |
| }; |
| </code></pre> |
| <p><strong>[MUST]</strong> Place no space between the function name and <code>(</code> in function declaration, expression of named function and function call.</p> |
| <pre><code class="lang-js">// good |
| |
| function funcName() { |
| } |
| |
| var funcName = function funcName() { |
| }; |
| |
| funcName(); |
| |
| |
| // bad |
| |
| function funcName () { |
| } |
| |
| var funcName = function funcName () { |
| }; |
| |
| funcName (); |
| </code></pre> |
| <p><strong>[MUST]</strong> Place no space between <code>,</code> and <code>;</code>.</p> |
| <pre><code class="lang-js">// good |
| callFunc(a, b); |
| |
| // bad |
| callFunc(a , b) ; |
| </code></pre> |
| <p><strong>[MUST]</strong> Place no space after <code>(</code> and <code>[</code> and before <code>)</code> and <code>]</code>.</p> |
| <pre><code class="lang-js">// good |
| |
| callFunc(param1, param2, param3); |
| |
| save(this.list[this.indexes[i]]); |
| |
| needIncream && (variable += increament); |
| |
| if (num > list.length) { |
| } |
| |
| while (len--) { |
| } |
| |
| |
| // bad |
| |
| callFunc( param1, param2, param3 ); |
| |
| save( this.list[ this.indexes[ i ] ] ); |
| |
| needIncreament && ( variable += increament ); |
| |
| if ( num > list.length ) { |
| } |
| |
| while ( len-- ) { |
| } |
| |
| |
| // good |
| var arr1 = []; |
| var arr2 = [1, 2, 3]; |
| var obj1 = {}; |
| var obj2 = {name: 'obj'}; |
| var obj3 = { |
| name: 'obj', |
| age: 20, |
| sex: 1 |
| }; |
| |
| // bad |
| var arr1 = [ ]; |
| var arr2 = [ 1, 2, 3 ]; |
| var obj1 = { }; |
| var obj2 = { name: 'obj' }; |
| var obj3 = {name: 'obj', age: 20, sex: 1}; |
| </code></pre> |
| <p><strong>[MUST]</strong> Must no trailing space in each line.</p> |
| <h3 id="line-break">Line Break</h3> |
| <p><strong>[MUST]</strong> Place line break in the end of a statement.</p> |
| <p><strong>[MUST]</strong> No more than 120 characters per line.</p> |
| <p><strong>[MUST]</strong> Place operator at the beginning of a line if it break lines.</p> |
| <pre><code class="lang-js">// good |
| if (user.isAuthenticated() |
| && user.isInRole('admin') |
| && user.hasAuthority('add-admin') |
| || user.hasAuthority('delete-admin') |
| ) { |
| // Code |
| } |
| |
| var result = number1 + number2 + number3 |
| + number4 + number5; |
| |
| |
| // bad |
| if (user.isAuthenticated() && |
| user.isInRole('admin') && |
| user.hasAuthority('add-admin') || |
| user.hasAuthority('delete-admin')) { |
| // Code |
| } |
| |
| var result = number1 + number2 + number3 + |
| number4 + number5; |
| </code></pre> |
| <p><strong>[MUST]</strong> Start a new line for <code>)</code>, <code>]</code>, <code>}</code> if the content inside the brackets occupies multiple lines. |
| Make the same indent as the line where the corresponding <code>(</code>, <code>[</code>, <code>{</code> placed.</p> |
| <pre><code class="lang-js">// good |
| if (product) { |
| product.load(); |
| if (user.isAuthenticated() |
| && user.isInRole('admin') |
| && user.hasAuthority('add-admin') |
| ) { |
| sendProduct(user, product); |
| } |
| } |
| var arr = [ |
| 'candy', 'sugar' |
| ]; |
| |
| // bad |
| if (product) { |
| product.load(); |
| if (user.isAuthenticated() |
| && user.isInRole('admin') |
| && user.hasAuthority('add-admin')) { |
| sendProduct(user, product); |
| } |
| } |
| var arr = [ |
| 'candy', 'sugar' |
| ]; |
| </code></pre> |
| <p><strong>[MUST]</strong> Must not break lines before <code>,</code> or <code>;</code>.</p> |
| <pre><code class="lang-js">// good |
| var obj = { |
| a: 1, |
| b: 2, |
| c: 3 |
| }; |
| |
| foo( |
| aVeryVeryLongArgument, |
| anotherVeryLongArgument, |
| callback |
| ); |
| |
| |
| // bad |
| var obj = { |
| a: 1 |
| , b: 2 |
| , c: 3 |
| }; |
| |
| foo( |
| aVeryVeryLongArgument |
| , anotherVeryLongArgument |
| , callback |
| ); |
| </code></pre> |
| <p><strong>[SUGGEST]</strong> Suggestion about line break and indent:</p> |
| <pre><code class="lang-js">if (user.isAuthenticated() |
| && user.isInRole('admin') |
| && user.hasAuthority('add-admin') |
| ) { |
| // Code |
| } |
| |
| foo( |
| aVeryVeryLongArgument, |
| anotherVeryLongArgument, |
| callback |
| ); |
| |
| baidu.format( |
| dateFormatTemplate, |
| year, month, date, hour, minute, second |
| ); |
| |
| $('#items') |
| .find('.selected') |
| .highlight() |
| .end(); |
| |
| var result = thisIsAVeryVeryLongCondition |
| ? resultA : resultB; |
| |
| var result = condition |
| ? thisIsAVeryVeryLongResult |
| : resultB; |
| </code></pre> |
| <p><strong>[MUST]</strong> Start a new line for <code>else</code> and <code>catch</code> if using multi-line blocks.</p> |
| <pre><code class="lang-js">// good |
| |
| if (condition) { |
| // some statements; |
| } |
| else { |
| // some statements; |
| } |
| |
| try { |
| // some statements; |
| } |
| catch (ex) { |
| // some statements; |
| } |
| |
| |
| // bad |
| |
| if (condition) { |
| // some statements; |
| } else { |
| // some statements; |
| } |
| |
| try { |
| // some statements; |
| } catch (ex) { |
| // some statements; |
| } |
| </code></pre> |
| <h3 id="statement">Statement</h3> |
| <p><strong>[MUST]</strong> The comma must not be ignored at the end of a statement.</p> |
| <p><strong>[MUST]</strong> The <code>{}</code> must not be ignored even if there is only one line.</p> |
| <pre><code class="lang-js">// good |
| if (condition) { |
| callFunc(); |
| } |
| |
| // bad |
| if (condition) callFunc(); |
| if (condition) |
| callFunc(); |
| </code></pre> |
| <p><strong>[MUST]</strong> Place no comma at the end of a function definition.</p> |
| <pre><code class="lang-js">// good |
| function funcName() { |
| } |
| |
| // bad |
| function funcName() { |
| }; |
| |
| // For function expression, the comma must not be ignored. |
| var funcName = function () { |
| }; |
| </code></pre> |
| <p><strong>[MUST]</strong> No trailing comma in object and array declarations.</p> |
| <pre><code class="lang-js">// good |
| |
| var obj = { |
| attr1: 'xxx', |
| attr2: 'yyy' |
| }; |
| |
| var arr = [ |
| 'xxx', |
| 'yyy' |
| ]; |
| |
| |
| // bad |
| |
| var obj = { |
| attr1: 'xxx', |
| attr2: 'yyy', |
| }; |
| |
| var arr = [ |
| 'xxx', |
| 'yyy', |
| ]; |
| </code></pre> |
| <h3 id="naming-conventions">Naming Conventions</h3> |
| <p><strong>[MUST]</strong> Use lowerCamelCase for variables, properties and function names.</p> |
| <pre><code class="lang-js">var loadingModules = {}; |
| function loadProduct() { |
| } |
| </code></pre> |
| <p><strong>[MUST]</strong> Use UpperCamelCase (Pascal) for class names.</p> |
| <pre><code class="lang-js">function Element(options) { |
| } |
| </code></pre> |
| <p><strong>[SUGGEST]</strong> All of the letters of a abbreviation should be both upper cases or both lower cases.</p> |
| <pre><code class="lang-js">function parseSVG() { |
| } |
| var svgParser; |
| </code></pre> |
| <h2 id="language-features">Language features</h2> |
| <h3 id="compatibility">Compatibility</h3> |
| <p><strong>[MUST]</strong> The JavaScript code of ECharts should be based on <code>ECMAScript Language Specification Edition 3 (ES3)</code>. The language features that not supported by ES3 (namely, features that only support by ES5, ES6 or upper versions) must not be used.</p> |
| <p>But there is an exception that ES Module can be used.</p> |
| <p>Language features can be polyfilled by some utilities, but must not by modifying the prototype of the built-in JS objects.</p> |
| <pre><code class="lang-js">// good |
| |
| import * as zrUtil from 'zrender/src/core/util'; |
| |
| zrUtil.each(array, function (val, index) { |
| sum += val; |
| }); |
| |
| var result = zrUtil.map(array, function (val) { |
| return parse(val); |
| }); |
| |
| var pos = zrUtil.indexOf(array, val); |
| |
| var obj2 = zrUtil.extend({}, obj1); |
| |
| function Element() { |
| // ... |
| } |
| |
| |
| // bad |
| |
| array.forEach(function (val, index) { |
| sum += val; |
| }); |
| |
| let result = array.map(function (val) { |
| return parse(val); |
| }); |
| |
| const pos = array.indexOf(val); |
| |
| var obj2 = Object.assign({}, obj1); |
| |
| class Element { |
| // ... |
| } |
| |
| |
| // Forbidden |
| |
| String.prototype.trim = function () { |
| }; |
| </code></pre> |
| <h3 id="variable">Variable</h3> |
| <p><strong>[MUST]</strong> Variables must be declared by <code>var</code>. And a <code>var</code> can not declares more than one variable.</p> |
| <pre><code class="lang-js">// good |
| var name = 'MyName'; |
| var hangModules = []; |
| var missModules = []; |
| var visited = {}; |
| |
| // bad |
| name = 'MyName'; |
| var hangModules = [], |
| missModules = [], |
| visited = {}; |
| </code></pre> |
| <h3 id="condition">Condition</h3> |
| <p><strong>[MUST]</strong> In equality expression, <code>==</code> can only be used on <code>null</code> or <code>undefined</code> detection. <code>===</code> should be used in the rest of cases .</p> |
| <pre><code class="lang-js">// good |
| if (age === 30) { |
| // ... |
| } |
| if (type == null) { |
| // ... |
| } |
| |
| // bad |
| if (age == 30) { |
| // ...... |
| } |
| </code></pre> |
| <p><strong>[SUGGEST]</strong> Use <code>xxx == null</code> to determine <code>null</code> or <code>undefined</code>.</p> |
| <p><strong>[SUGGEST]</strong> Try best to make the meaning of <code>null</code> and <code>undefined</code> the same, namely, do not make users or developers distinguishing whether a variable is <code>null</code> or <code>undefined</code>.</p> |
| <p><strong>[SUGGEST]</strong> The function expression or function declaration should not be placed inside a loop body.</p> |
| <pre><code class="lang-js">// good |
| function clicker() { |
| // ...... |
| } |
| |
| for (var i = 0, len = elements.length; i < len; i++) { |
| var element = elements[i]; |
| addListener(element, 'click', clicker); |
| } |
| |
| |
| // bad |
| for (var i = 0, len = elements.length; i < len; i++) { |
| var element = elements[i]; |
| addListener(element, 'click', function () {}); |
| } |
| </code></pre> |
| <h3 id="type-conversion">Type Conversion</h3> |
| <p><strong>[SUGGEST]</strong> Use <code>+ ''</code> to convert a value to string.</p> |
| <pre><code class="lang-js">// good |
| num + ''; |
| |
| // bad |
| new String(num); |
| num.toString(); |
| String(num); |
| </code></pre> |
| <p><strong>[SUGGEST]</strong> Use <code>+</code> to convert a value to number.</p> |
| <pre><code class="lang-js">// good |
| +str; |
| |
| // bad |
| Number(str); |
| </code></pre> |
| <p><strong>[MUST]</strong> The second parameter must not be ignored when using <code>parseInt</code>.</p> |
| <pre><code class="lang-js">// good |
| parseInt(str, 10); |
| |
| // bad |
| parseInt(str); |
| </code></pre> |
| <h3 id="string-object-array">String, Object, Array</h3> |
| <p><strong>[MUST]</strong> Use <code>'</code> but not <code>"</code> to define a string.</p> |
| <p><strong>[MUST]</strong> Use object literal <code>{}</code> to create a plain object.</p> |
| <pre><code class="lang-js">// good |
| var obj = {}; |
| |
| // bad |
| var obj = new Object(); |
| </code></pre> |
| <p><strong>[MUST]</strong> If all of the properties of an object literal do not need quotation marks, they should ignore them. If quotation marks is necessary, use <code>'</code> but not <code>"</code>.</p> |
| <pre><code class="lang-js">// good |
| var info = { |
| name: 'someone', |
| age: 28 |
| }; |
| |
| // bad |
| var info = { |
| 'name': 'someone', |
| 'age': 28 |
| }; |
| var info2 = { |
| "age": 40 |
| }; |
| </code></pre> |
| <p><strong>[MUST]</strong> The prototype of built-in objects must not be modified.</p> |
| <pre><code class="lang-js">// Forbidden |
| String.prototype.trim = function () { |
| }; |
| </code></pre> |
| <p><strong>[SUGGEST]</strong> Try best to use <code>.</code> but not <code>[]</code> to visit properties of an object.</p> |
| <p><strong>[SUGGEST]</strong> <code>hasOwnProperty</code> should be used to when using <code>for ... in ...</code>, in case that some extra properties is added on the prototype of <code>Object</code> in some runtime environment.</p> |
| <pre><code class="lang-js">var newInfo = {}; |
| for (var key in info) { |
| if (info.hasOwnProperty(key)) { |
| newInfo[key] = info[key]; |
| } |
| } |
| </code></pre> |
| <p><strong>[MUST]</strong> Use array literal <code>[]</code> to create an array, except intending to create an array with a given length.</p> |
| <pre><code class="lang-js">// good |
| var arr = []; |
| var arr2 = new Array(1e4); |
| |
| // bad |
| var arr = new Array(); |
| </code></pre> |
| <p><strong>[MUST]</strong> Do not use <code>for in</code> in array traverse.</p> |
| <h3 id="others">Others</h3> |
| <p><strong>[MUST]</strong> Do not use <code>eval</code> and <code>with</code>. <code>new Function</code> can be used.</p> |
| |
| </div> |
| |
| <script type="text/javascript"> |
| |
| $('.markdown-body pre code').each(function (index, el) { |
| $(el).addClass('prettyprint'); |
| }); |
| prettyPrint(); |
| |
| </script> |
| </body> |
| </html> |