variables [coercion] variables and functions must be defined before they are used.
// goodvar name = ‘MyName‘;// badname = ‘MyName‘;
[Force] Only one variable can be declared per var.
Explain:
A var declares multiple variables, which can lead to a longer length of the head and can easily confuse commas and semicolons when modified.
// goodvar hangModules = [];var missModules = [];var visited = {};// badvar hangModules = [], missModules = [], visited = {};
The [force] variable must be declared as a declaration, and all variables should not be declared uniformly at the beginning of a function or other form of code block.
Explain:
The farther away the variable declaration is from the use, the greater the span that occurs, and the higher the cost of reading and maintaining the code. Although the variable of JavaScript is the function scope, it should be based on the intention of programming, to reduce the distance space of the variable appears.
// goodfunction kv2List(source) { var list = []; for (var key in source) { if (source.hasOwnProperty(key)) { var item = { k: key, v: source[key] }; list.push(item); } } return list;}// badfunction kv2List(source) { var list = []; var key; var item; for (key in source) { if (source.hasOwnProperty(key)) { item = { k: key, v: source[key] }; list.push(item); } } return list;}
The condition [force] uses a strict type = = = in equality Expression. Use = = NULL is allowed only when null or undefined is judged.
Explain:
Use = = To avoid the equivalent of implicit type conversions in the judgment.
// goodif (age === 30) { // ......}// badif (age == 30) { // ......}
[Recommended] Use concise expressions whenever possible.
// 字符串为空// goodif (!name) { // ......}// badif (name === ‘‘) { // ......}
// 字符串非空// goodif (name) { // ......}// badif (name !== ‘‘) { // ......}
// 数组非空// goodif (collection.length) { // ......}// badif (collection.length > 0) { // ......}
// 布尔不成立// goodif (!notTrue) { // ......}// badif (notTrue === false) { // ......}
// null 或 undefined// goodif (noValue == null) { // ......}// badif (noValue === null || typeof noValue === ‘undefined‘) { // ......}
[Recommended] The order in which the branches are arranged by the frequency of execution.
Explain:
The order of the branches by frequency of execution benefits are:
People who read are easy to find the most common situations and increase readability.
Improve execution efficiency.
[Recommended] Use switch instead of if for multivalued conditions of the same variable or expression.
// goodswitch (typeof variable) { case ‘object‘: // ...... break; case ‘number‘: case ‘boolean‘: case ‘string‘: // ...... break;}// badvar type = typeof variable;if (type === ‘object‘) { // ......}else if (type === ‘number‘ || type === ‘boolean‘ || type === ‘string‘) { // ......}
[Recommended] If there are no statements after the function or the else block in the global, you can delete else.
// goodfunction getName() { if (name) { return name; } return ‘unnamed‘;}// badfunction getName() { if (name) { return name; } else { return ‘unnamed‘; }}
loops [Recommended] do not include a function expression in the loop body, in advance to extract the function outside the loop body.
Explain:
A function expression in the loop body that generates a loop-count function object during the run.
// goodfunction clicker() { // ......}for (var i = 0, len = elements.length; i < len; i++) { var element = elements[i]; addListener(element, ‘click‘, clicker);}// badfor (var i = 0, len = elements.length; i < len; i++) { var element = elements[i]; addListener(element, ‘click‘, function () {});}
[Recommended] for repeated use within the loop constant value, in the loop external variables cache.
// goodvar width = wrap.offsetWidth + ‘px‘;for (var i = 0, len = elements.length; i < len; i++) { var element = elements[i]; element.style.width = width; // ......}// badfor (var i = 0, len = elements.length; i < len; i++) { var element = elements[i]; element.style.width = wrap.offsetWidth + ‘px‘; // ......}
[Recommended] When iterating over an ordered set, the cache length.
Explain:
While modern browsers cache array lengths, for some host objects and arrays of older browsers, the number of elements is dynamically calculated each time the length is accessed, and the length of the cache can effectively improve program performance.
for (var i = 0, len = elements.length; i < len; i++) { var element = elements[i]; // ......}
[Recommended] Use reverse-order traversal when ordering an ordered set of independent traversal.
Explain:
Reverse traversal can save variables and optimize code.
var len = elements.length;while (len--) { var element = elements[len]; // ......}
Type type detection [recommended] type detection takes precedence over the use of typeof. Object type detection uses instanceof. Null or undefined detection uses = = NULL.
// stringtypeof variable === ‘string‘// numbertypeof variable === ‘number‘// booleantypeof variable === ‘boolean‘// Functiontypeof variable === ‘function‘// Objecttypeof variable === ‘object‘// RegExpvariable instanceof RegExp// Arrayvariable instanceof Array// nullvariable === null// null or undefinedvariable == null// undefinedtypeof variable === ‘undefined‘
type conversion [recommended] when converting to string, use + '.
// goodnum + ‘‘;// badnew String(num);num.toString();String(num);
[Recommended] When converting to number, use + is usually used.
// good+str;// badNumber(str);
[Recommended] string is converted to number, the end of the string to be converted contains a non-numeric and is expected to be ignored, use parseint.
var width = ‘200px‘;parseInt(width, 10);
[Force] When using parseint, you must specify a binary.
// goodparseInt(str, 10);// badparseInt(str);
When converting to Boolean, use the!!。
var num = 3.14;!!num;
[Recommended] Number remove decimal point, use Math.floor/math.round/math.ceil, do not use parseint.
// goodvar num = 3.14;Math.ceil(num);// badvar num = 3.14;parseInt(num, 10);
string [Force] string start and end using single quotation marks.
Explain:
Entering single quotes does not require holding down shift for easy entry.
In practical use, strings are often used to stitch together HTML. To facilitate the inclusion of double quotation marks in HTML without the need for escape notation.
var str = ‘我是一个字符串‘;var html = ‘<div class="cls">拼接HTML可以省去双引号转义</div>‘;
[Recommended] use arrays or + stitching strings.
Explain:
Using the + stitching string, if all of the stitching is stringliteral, the compression tool can be optimized for automatic merging. Therefore, the static string is recommended to use + stitching.
In modern browsers, using the + stitching string, the performance of the array is higher than the way.
If you need to take care of the old browser, try to use array stitching strings.
// 使用数组拼接字符串var str = [ // 推荐换行开始并缩进开始第一个字符串, 对齐代码, 方便阅读. ‘<ul>‘, ‘<li>第一项</li>‘, ‘<li>第二项</li>‘, ‘</ul>‘].join(‘‘);// 使用 `+` 拼接字符串var str2 = ‘‘ // 建议第一个为空字符串, 第二个换行开始并缩进开始, 对齐代码, 方便阅读 + ‘<ul>‘, + ‘<li>第一项</li>‘, + ‘<li>第二项</li>‘, + ‘</ul>‘;
[Recommended] The use of string concatenation to generate HTML, need to be based on the context of a reasonable escape.
Explain:
Strings that are spliced in JavaScript and will eventually be output to the page need to be properly escaped to prevent security breaches. The following example code is a scenario description and cannot be run directly.
// HTML 转义var str = ‘<p>‘ + htmlEncode(content) + ‘</p>‘;// HTML 转义var str = ‘<input type="text" value="‘ + htmlEncode(value) + ‘">‘;// URL 转义var str = ‘<a href="/?key=‘ + htmlEncode(urlEncode(value)) + ‘">link</a>‘;// JavaScript字符串 转义 + HTML 转义var str = ‘<button onclick="check(\‘‘ + htmlEncode(strLiteral(name)) + ‘\‘)">提交</button>‘;
Object [Force] uses the object literal {} to create a new object.
// goodvar obj = {};// badvar obj = new Object();
When creating an object, it is recommended that all properties do not add quotation marks if all properties of an object can be unquoted.
var info = { name: ‘someone‘, age: 28};
When the object is created, if any one of the attributes needs to be added with quotation marks, all properties are suggested to add '.
Explain:
If the attributes are not in the form of Identifier and numberliteral, they need to be provided in stringliteral form.
// goodvar info = { ‘name‘: ‘someone‘, ‘age‘: 28, ‘more-info‘: ‘...‘};// badvar info = { name: ‘someone‘, age: 28, ‘more-info‘: ‘...‘};
When you access the [Recommended] property, try to use:
Explain:
Attribute names conform to the requirements of Identifier and can be passed. To access it, otherwise it can only be accessed through [expr].
Objects that are typically declared in JavaScript are named using the Camel name method. To access more clearly and concisely. Some special attributes, such as JSON from the back end, may be named in an unusual way and can be accessed through [expr].
info.age;info[‘more-info‘];
[Recommended] Use hasOwnProperty to filter out the properties in the prototype when traversing the object.
var newInfo = {};for (var key in info) { if (info.hasOwnProperty(key)) { newInfo[key] = info[key]; }}
Array [Force] creates a new array using the array literal [] unless you want to create an array of the specified length.
// goodvar arr = [];// badvar arr = new Array();
The [force] traversal array does not use the for in.
Explain:
Array objects may have properties other than numbers, in which case in does not get the correct result.
var arr = [‘a‘, ‘b‘, ‘c‘];// 这里仅作演示, 实际中应使用 Object 类型arr.other = ‘other things‘;// 正确的遍历方式for (var i = 0, len = arr.length; i < len; i++) { console.log(i);}// 错误的遍历方式for (var i in arr) { console.log(i);}
[Recommended] Not for performance reasons to implement the array sorting function, try to use the array of the sort method.
Explain:
Its own implementation of the regular sorting algorithm, performance is not superior to the array default sort method. The following two scenarios allow you to sort by yourself:
A stable sorting algorithm is required to achieve strict and consistent sorting results.
The data feature is distinct and suitable for use in buckets.
[Recommended] empty array using. Length = 0. function function length [recommended] The length of a function is controlled within 50 lines.
Explain:
Mixing too many logical units in a large function can easily lead to difficult maintenance. A clear and understandable function should complete a single logical unit. Complex operations should be further extracted, using function invocation to reflect the process.
An indivisible logic such as a particular algorithm allows exceptions.
function syncViewStateOnUserAction() { if (x.checked) { y.checked = true; z.value = ‘‘; } else { y.checked = false; } if (a.value) { warning.innerText = ‘‘; submitButton.disabled = false; } else { warning.innerText = ‘Please enter it‘; submitButton.disabled = true; }}// 直接阅读该函数会难以明确其主线逻辑,因此下方是一种更合理的表达方式:function syncViewStateOnUserAction() { syncXStateToView(); checkAAvailability();}function syncXStateToView() { y.checked = x.checked; if (x.checked) { z.value = ‘‘; }}function checkAAvailability() { if (a.value) { clearWarnignForA(); } else { displayWarningForAMissing(); }}
[Recommended] The parameters of a function are controlled within 6.
Explain:
In addition to the variable length parameters, the function has different logical meaning of the parameters proposed to control within 6, too many parameters will cause the maintenance difficulty increases.
In some cases, when multiple modules are loaded using AMD Loader require, their callback may have more parameters, so there is no mandatory limit on the number of function arguments.
[Recommended] Pass the options parameter to non-data input type parameters.
Explain:
Some function parameters are not input to the algorithm, but are used to determine certain branch conditions of the algorithm, which are suggested to be passed through an options parameter.
The following functions:
/** * 移除某个元素 * * @param {Node} element 需要移除的元素 * @param {boolean} removeEventListeners 是否同时将所有注册在元素上的事件移除 */function removeElement(element, removeEventListeners) { element.parent.removeChild(element); if (removeEventListeners) { element.clearEventListeners(); }}
Can be converted to the following signature:
/** * 移除某个元素 * * @param {Node} element 需要移除的元素 * @param {Object} options 相关的逻辑配置 * @param {boolean} options.removeEventListeners 是否同时将所有注册在元素上的事件移除 */function removeElement(element, options) { element.parent.removeChild(element); if (options.removeEventListeners) { element.clearEventListeners(); }}这种模式有几个显著的优势:boolean 型的配置项具备名称,从调用的代码上更易理解其表达的逻辑意义。当配置项有增长时,无需无休止地增加参数个数,不会出现 removeElement(element, true, false, false, 3) 这样难以理解的调用代码。当部分配置参数可选时,多个参数的形式非常难处理重载逻辑,而使用一个 options 对象只需判断属性是否存在,实现得以简化。
Closures [recommended] Set the large object in the closure to null at the appropriate time.
Explain:
In JavaScript, closures can be used without special keywords, and a function can arbitrarily access variables that are outside the scope of its definition. It is important to note that the scope of the function is static, which is determined at the time of definition and has no relation to the timing and manner of the call.
Closures prevent the garbage collection of some variables, which can cause all external variables to be recycled for older JavaScript engines.
First, a more definitive conclusion is that the following will affect the recycling of variables within closures:
Whether the variable is used in a nested function.
Whether there is a direct call to eval in the nested function.
Whether the with expression is used.
Chakra, V8 and SpiderMonkey will be affected by the above factors, showing different and more similar recovery strategies, and JScript.dll and Carakan are completely without this optimization, will be intact to retain the entire lexicalenvironment All variables in the binding, resulting in a certain amount of memory consumption.
Since the behavior of the Chakra, V8, and SpiderMonkey engines, which have a recycling optimization strategy for the inner variables of the closure, is similar, it can be summarized as follows when a function FN is returned:
- If FN's [[Scope]] is objectenvironment (with expression generation objectenvironment, function and catch expression generates Declarativeenvironment):
If it is the V8 engine, exit the whole process.
If it is SpiderMonkey, the outer lexicalenvironment of the objectenvironment is processed.
- Gets all objects of type function under the current lexicalenvironment, and for each function object, parses its functionbody:
If the functionbody contains a direct call to Eval, exit the whole process.
Otherwise get all the Identifier.
For each of the Identifier, set it to name, and from the lexicalenvironment, find the bind binding that is named Name, based on the rules referenced by the lookup variable.
Add the Notswap property to the binding with a value of true.
- Checks each variable binding in the current lexicalenvironment, if the binding has a Notswap property and the value is true:
If it is the V8 engine, remove the binding.
If it is SpiderMonkey, the value of the binding is set to undefined, and the Notswap property is removed.
For the Chakra engine, it is not possible to know whether to press V8 mode or SpiderMonkey mode.
If you have a very large object and are expected to execute in an older engine, when using closures, be careful to place objects that are not required by the closure into a null reference.
[Recommended] Use Iife to avoid Lift effects.
Explain:
When you reference a function external variable, the value of the external variable is determined by the runtime rather than the definition when the function executes, the most typical scenario is as follows:
var tasks = [];for (var i = 0; i < 5; i++) { tasks[tasks.length] = function () { console.log(‘Current cursor is at ‘ + i); };}var len = tasks.length;while (len--) { tasks[len]();}
The above code executes all functions in the tasks to output the current cursor was at 5, which often does not meet the expectations.
This phenomenon is called the Lift effect. The workaround is to unbind the variable by adding an extra layer of closure function to pass the required external variables as parameters:
var tasks = [];for (var i = 0; i < 5; i++) { // 注意有一层额外的闭包 tasks[tasks.length] = (function (i) { return function () { console.log(‘Current cursor is at ‘ + i); }; })(i);}var len = tasks.length;while (len--) { tasks[len]();}
The [recommended] NULL function does not use the form of new function ().
var emptyFunction = function () {};
[Recommended] for high performance requirements, it is recommended to have a constant of an empty function for sharing in multiple places.
var EMPTY_FUNCTION = function () {};function MyClass() {}MyClass.prototype.abstractMethod = EMPTY_FUNCTION;MyClass.prototype.hooks.before = EMPTY_FUNCTION;MyClass.prototype.hooks.after = EMPTY_FUNCTION;
The inheritance scheme of the object-oriented [coercion] class, which needs to be corrected when implemented constructor
Explain:
Typically, class succession scenarios using other libraries will be constructor corrected. If you implement your own class-inheriting scheme, you need to make constructor corrections.
/** * 构建类之间的继承关系 * * @param {Function} subClass 子类函数 * @param {Function} superClass 父类函数 */function inherits(subClass, superClass) { var F = new Function(); F.prototype = superClass.prototype; subClass.prototype = new F(); subClass.prototype.constructor = subClass;}
[Recommended] When declaring a class, ensure the correctness of constructor.
function Animal(name) { this.name = name;}// 直接prototype等于对象时,需要修正constructorAnimal.prototype = { constructor: Animal, jump: function () { alert(‘animal ‘ + this.name + ‘ jump‘); }};// 这种方式扩展prototype则无需理会constructorAnimal.prototype.jump = function () { alert(‘animal ‘ + this.name + ‘ jump‘);};
The recommended property is declared in the constructor, and the method is declared in the prototype.
Explain:
The members of the prototype object are shared by all instances and can save memory consumption. So we should follow this principle when coding: The prototype object contains members that the program does not modify, such as method functions or configuration items.
function TextNode(value, engine) { this.value = value; this.engine = engine;}TextNode.prototype.clone = function () { return this;};
The event name for the custom event must be all lowercase.
Explain:
In JavaScript's widely used browser environment, most DOM event names are all lowercase. To follow the habits of most JavaScript developers, the event name should be all lowercase when designing custom events.
[Force] A custom event can have only one event parameter. If the event requires more information to be passed, the event object should be carefully designed.
Explain:
The benefits of an event object are:
Order-independent, avoiding event listeners needing to memorize parameter order.
Each event information can be provided or not provided as needed, and more freely.
Easy to expand and add event information in the future without thinking about breaking
[Recommended] When designing custom events, consider prohibiting the default behavior.
Explain:
There are two common ways to disallow default behavior:
return false in the event listener function.
The event object contains methods that prohibit the default behavior, such as Preventdefault.
Prohibit use of Evail recommend using the new Function () [recommended] to reduce the use of delete.
Explain:
If there is no special need, reduce or avoid the use of delete. The use of delete destroys the performance optimizations of some JavaScript engines.
It is recommended to handle the exceptions that the delete might produce.
Explain:
For an object that has a value that is traversed, and the value null is considered to have business logic, removing a property must use the delete operation.
When using delete in strict mode or IE, a property that cannot be deleted throws an exception, so it is recommended to add a try-catch block if you are unsure whether the property can be deleted.
try { delete o.x;}catch (deleteError) { o.x = null;}
Avoid modifying externally-passed objects.
Explain:
Because of the dynamic nature of its scripting language, JavaScript can add, delete, and modify property values when an object is not seal or freeze.
However, it is easy to modify the objects that are not self-controlling, which can cause the code to have problems in unpredictable situations. Therefore, well-designed components and functions should avoid modifications to externally-passed objects.
The Selectnode method of the following code modifies an externally passed-in DataSource object. If DataSource is used on other occasions (such as another Tree instance), it can cause confusion in the state.
function Tree(datasource) { this.datasource = datasource;}Tree.prototype.selectNode = function (id) { // 从datasource中找出节点对象 var node = this.findNode(id); if (node) { node.selected = true; this.flushView(); }};
For such scenarios, it is necessary to use an additional object to maintain the selected state of the node using the Selectednodeindex object, which is controlled by itself and does not produce any interaction with the outside, and does not make any modifications to the datasource.
function Tree(datasource) { this.datasource = datasource; this.selectedNodeIndex = {};}Tree.prototype.selectNode = function (id) { // 从datasource中找出节点对象 var node = this.findNode(id); if (node) { this.selectedNodeIndex[id] = true; this.flushView(); }};除此之外,也可以通过 deepClone 等手段将自身维护的对象与外部传入的分离,保证不会相互影响。
Dom[recommended] For a single element, use document.getElementById as much as possible to avoid using document.all. [Recommended] For a collection of multiple elements, use Context.getelementsbytagname to get as much as possible. Where the context can be document or other element. Specifies that the TagName parameter is * To obtain all child elements. [Recommended] When iterating through the collection of elements, try to cache the collection length. If you want to manipulate the same collection multiple times, you should convert the collection to an array. [Recommended] To get the immediate child elements of an element, use children. Avoid using childnodes unless expected to include nodes that contain text, comments, and attribute types.
Explain:
The result of the native FETCH element collection does not directly reference the DOM element, but rather reads the index, so changes to the DOM structure are reflected in the results in real time.
<div></div><span></span><script>var elements = document.getElementsByTagName(‘*‘);// 显示为 DIValert(elements[0].tagName);var div = elements[0];var p = document.createElement(‘p‘);docpment.body.insertBefore(p, div);// 显示为 Palert(elements[0].tagName);</script>
[Recommended] You should use getComputedStyle or currentstyle when getting the actual style information for the element.
Explain:
Styles can only be obtained by using a style that is inline defined or directly set through JavaScript. Element styles set through CSS class cannot be obtained directly from the style.
[Recommended] As far as possible by adding a predefined className elements to change the element style, avoid the direct manipulation of the style setting [mandatory] When you set the element style through the style object, for attributes with a value other than 0, the unit is not allowed to be omitted.
Explain:
In addition to IE, standard browsers ignore non-canonical attribute values, leading to compatibility issues.
[Recommended] When manipulating the DOM, minimize page reflow.
Explain:
Page Reflow is a very time-consuming behavior that can easily lead to performance bottlenecks. Some of the following scenarios trigger the browser's reflow:
- addition, modification (content), deletion of DOM elements.
- Apply a new style or modify any properties that affect the layout of the element.
- Resize browser window, scrolling page.
Reads some attributes of an element (Offsetleft, OffsetTop, offsetheight, offsetwidth, Scrolltop/left/width/height, clienttop/left/width/ Height, getComputedStyle (), Currentstyle (in IE).
[Recommended] Minimize DOM operations.Explain:
Dom manipulation is also a very time-consuming operation, and reducing DOM operations can help improve performance. To give a simple example, build a list. We can do this in two ways:
CreateElement in the loop body and append into the parent element.
The HTML string is stitched in the loop body, and the InnerHTML of the parent element is written at the end of the loop.
The first method looks more standard, but each cycle operates on the DOM, with very little performance. The second method is recommended here.
JavaScript Coding Specification (2)