Front-end coding specification: javascript specification

Keywords: Javascript github JQuery Programming

Global Namespace Pollution and IIFE

Always wrap the code into an IIFE (Immediately-Invoked Function Expression) to create an isolated domain of definitions. This approach prevents global namespaces from being polluted.

IIFE also ensures that your code is not easily modified by code in other global namespaces (such as third-party libraries, window references, overwritten undefined keywords, etc.).

Not recommended

var x = 10,
    y = 100;
console.log(window.x + ' ' + window.y);

Recommend

(function(w){
    'use strict';
    var x = 10,
        y = 100;
    w.console.log((w.x === undefined) + ' ' + (w.y === undefined));
}(window));

IIFE (immediate functional expression)

Whenever you want to create a new closed domain, use IIFE. It not only avoids interference, but also releases memory immediately after execution.

All script files are recommended to start with IIFE.

The execution brackets of immediately executed function expressions should be written in the outer inclusion brackets. Writing in or out is valid, but writing in makes the whole expression look more like a whole, so it is recommended to do so.

Not recommended

(function(){})();

Recommend

(function(){}());

So format your IIFE code in the following way:

(function($, w, d){
    'use strict';
    $(function() {
        w.alert(d.querySelectorAll('div').length);
    });
}(jQuery, window, document));

Strict mode

ECMAScript5 Strict Mode can be activated throughout the script or in a single method. It does more rigorous error checking for different javascript contexts. Strict patterns also ensure that javascript code is more robust and runs faster.

Strict patterns prevent the use of reserved keywords that are likely to be introduced in the future.

You should enable strict mode in your script, preferably in a stand-alone IIFE. Avoid using it in the first line of your script and cause all your scripts to start in strict mode, which may cause problems with third-party libraries.

Not recommended

'use strict';
(function(){
    // some code
}());

Recommend

(function(){
    'use strict';
    // some code
}());

Variable declaration

Always use var to declare variables. If var is not specified, variables will be implicitly declared as global variables, which will be difficult to control. Without a declaration, it becomes unclear what domain the variable is in (either in Document or Window s, or easily accessible to the local domain). So always use var to declare variables.

The advantage of strict mode is that when you enter the wrong variable name by mistake, it can help you locate the source of the error through error information.

Not recommended

x = 10;
y = 100;

Recommend

var x = 10,
    y = 100;

Understanding the definition domain of javascript

Variables and method definitions in JavaScript are automatically promoted to before execution. JavaScript has only a function-level domain, not a block domain in many other programming languages, so you define a variable in a statement and loop body within a function that acts on the whole function, not just in the statement or loop body, because their declarations are automatically promoted by javascript.

Let's take an example to see what this is all about.

Original function

(function(){
    'use strict';
    var a = 10;
    for(var i = 0; i < a; i++) {
        var b = i * i;
        console.log(b);
    }
    if(a === 10) {
        var f = function() {
            console.log(a);
        };
        f();
    }
    function x() {
        console.log('Mr. X!');
    }
    x();
}());

Upgraded by js

(function(){
    'use strict';
    var a,
        i,
        b,
        f;
    function x() {
        console.log('Mr. X!');
    }
  
    a = 10;
    for(i = 0; i < a; i++) {
        b = i * i;
        console.log(b);
    }
    if(a === 10) {
        f = function() {
            console.log(a);
        };
        f();
    }
    x();
}());

Based on the above promotion process, can you understand the following code?

Valid code

(function(){
    'use strict';
    var a = 10;
    i = 5;
    x();
    for(var i; i < a; i++) {
        console.log(b);
        var b = i * i;
    }
    if(a === 10) {
        f = function() {
            console.log(a);
        };
        f();
        var f;
    }
    function x() {
        console.log('Mr. X!');
    }
}());

As you can see, this confusing and misleading piece of code leads to unexpected results. Only good declaration habits, which we will mention in the next chapter, can avoid such erroneous risks as much as possible.

Statement advance

In order to avoid misunderstandings caused by automatic escalation of the definition of variables and methods described in the previous chapter and minimize risks, we should declare variables and methods manually and explicitly. That is to say, all variables and methods should be defined in the first line of the function.

Only one var keyword is declared, and multiple variables are separated by commas.

Not recommended

(function(){
    'use strict';
    
    var a = 10;
    var b = 10;
    
    for(var i = 0; i < 10; i++) {
        var c = a * b * i;
    }
    
    function f() {
    
    }
    
    var d = 100;
    var x = function() {
        return d * d;
    };
    console.log(x());
}());

Recommend

(function(){
    'use strict';
    
    var a = 10,
        b = 10,
        i,
        c,
        d,
        x;
    
    function f() {
    
    }
    
    for(i = 0; i < 10; i++) {
        c = a * b * i;
    }
    
    d = 100;
    x = function() {
        return d * d;
    };
    console.log(x());
}());

Write the assignment in the variable declaration as much as possible.

Not recommended

var a,
    b,
    c;
  
a = 10;
b = 10;
c = 100;

Recommend

 

var a = 10,
    b = 10,
    c = 100;

Using Hungarian nomenclature

Use hungarian notation To name variables.

Always use comparative judgments with type judgments

Always use the === precise comparison operator to avoid the hassle caused by the mandatory type conversion of javascript in the judgment process.

If you use the === operator, both sides of the comparison must be of the same type to be valid.

In the case of using only ==, the mandatory type conversion brought about by javascript complicates the tracking of judgement results. The following example shows how strange such results are:

(function(){
    'use strict';
    
    console.log('0' == 0); // true
    console.log('' == false); // true
    console.log('1' == true); // true
    console.log(null == undefined); // true
    
    var x = {
        valueOf: function() {
            return 'X';
        }
    };
    
    console.log(x == 'X');
}());

Wise Use of Truth and False Judgments

When we use variables or expressions in an if conditional statement, we make a true or false judgement. If (a = true) is different from if(a). The latter's judgment is special, we call it true or false judgment. This judgment is converted to true or false by special operations. The following expressions all return false: false, 0, undefined, null, NaN,'(empty string). This kind of true or false judgment is very helpful when we only want the result but don't care about the process.

The following examples show how true and false judgments work:

(function(){
    'use strict';
    
    function logTruthyFalsy(expr) {
        if(expr) {
            console.log('truthy');
        } else {
            console.log('falsy');
        }
    }
    
    logTruthyFalsy(true); // truthy
    logTruthyFalsy(1); // truthy
    logTruthyFalsy({}); // truthy
    logTruthyFalsy([]); // truthy
    logTruthyFalsy('0'); // truthy
    
    logTruthyFalsy(false); // falsy
    logTruthyFalsy(0); // falsy
    logTruthyFalsy(undefined); // falsy
    logTruthyFalsy(null); // falsy
    logTruthyFalsy(NaN); // falsy
    logTruthyFalsy(''); // falsy
}());

Logical operation in variable assignment

The logical operators | | and & & can also be used to return Boolean values. If the operation object is a non-Boolean object, then each expression will be judged from left to right. Based on this operation, eventually an expression is returned. This can be used to simplify your code when assigning variables.

Not recommended

if(!x) {
    if(!y) {
        x = 1;
    } else {
        x = y;
    }
}

Recommend

x = x || y || 1;

This little trick is often used to set default parameters for methods.

(function(){
    'use strict';
    
    function multiply(a, b) {
        a = a || 1;
        b = b || 1;
        
        console.log('Result ' + a * b);
    }
    
    multiply(); // Result 1
    multiply(10); // Result 10
    multiply(3, NaN); // Result 3
    multiply(9, 5); // Result 45
}());

semicolon

Semicolons are always used because implicit nesting of code can cause problems that are difficult to detect. Of course, we should put an end to these problems fundamentally.

Statements in javascript end with a semicolon, otherwise it will continue to execute, no matter whether it changes lines or not.

Clarification: semicolons and functions

A semicolon needs to be used at the end of an expression, not at the end of a function declaration. The best example to distinguish them is:

var foo = function() {
    return true;
};

function foo() {
    return true;
}

Nested function

Nested functions are very useful, for example, in tasks that continually create and hide auxiliary functions. You can use them very freely.

Function declarations within statement blocks

Never declare a function in a statement block, which is illegal in the strict mode of ECMAScript5. Function declarations should be at the top level of the domain. But in the statement block, function declaration can be converted into function expression and assigned to variables.

Not recommended

if (x) {
    function foo() {}
}

Recommend

if (x) {
    var foo = function() {};
}

abnormal

Basically, you can't avoid exceptions, especially when doing large-scale development (using application development frameworks, etc.).

In the absence of custom exceptions, returning error messages from functions with return values must be very tricky, let alone less elegant. Poor solutions include passing on the first reference type to receive error messages, or always returning a list of objects containing possible error objects. Basically, the above methods are relatively simple exception handling methods. Custom exception handling can be done at the right time.

In complex environments, you can consider throwing objects rather than just strings (default throw values).

if(name === undefined) {
    throw {
        name: 'System Error',
        message: 'A name should always be specified!'
    }
}

Standard characteristics

Always give priority to using standard features. To maximize scalability and compatibility, standard features are always preferred over non-standard features (e.g. preferred string.charAt(3) rather than string[3]; preferred DOM methods of operation to obtain element references rather than a specific shortcut for an application).

Simple Inheritance of Prototype

If you want to inherit your object in javascript, follow a simple pattern to create this inheritance. If you expect to encounter inheritance from complex objects, consider using an inheritance library, such as Proto.js by Axel Rauschmayer.

Simple inheritance should be done in the following ways:

(function(){
    'use strict';
    
    // Constructor function
    function Apple(name) {
        this.name = name;
    }
    // Defining a method of apple
    Apple.prototype.eat = function() {
        console.log('Eating ' + this.name);
    };
    
    // Constructor function
    function GrannySmithApple() {
        // Invoking parent constructor
        Apple.prototype.constructor.call(this, 'Granny Smith');
    }
    // Set parent prototype while creating a copy with Object.create
    GrannySmithApple.prototype = Object.create(Apple.prototype);
    // Set constructor to the sub type, otherwise points to Apple
    GrannySmithApple.prototype.constructor = GrannySmithApple;
    
    // Calling a super method
    GrannySmithApple.prototype.eat = function() {
        // Be sure to apply it onto our current object with call(this)
        Apple.prototype.eat.call(this);
        
        console.log('Poor Grany Smith');
    };
    
    // Instantiation
    var apple = new Apple('Test Apple');
    var grannyApple = new GrannySmithApple();
    
    console.log(apple.name); // Test Apple
    console.log(grannyApple.name); // Granny Smith
    
    // Instance checks
    console.log(apple instanceof Apple); // true
    console.log(apple instanceof GrannySmithApple); // false
    
    console.log(grannyApple instanceof Apple); // true
    console.log(grannyApple instanceof GrannySmithApple); // true
    
    // Calling method that calls super method
    grannyApple.eat(); // Eating Granny Smith\nPoor Grany Smith
}());

Use closure

Closure creation is perhaps the most useful and easily overlooked capability of js. A Reasonable Explanation of How Closures Work.

Never create a function in a loop

It is very easy to form closures by adding functions to simple loop statements. The following example is a typical pitfall:

Not recommended

(function(w){
    'use strict';
    
    var numbers = [1, 2, 3],
        i;
    
    for(i = 0; i < numbers.length; i++) {
        w.setTimeout(function() {
            w.alert('Index ' + i + ' with number ' + numbers[i]);
        }, 0);
    }
}(window));

The next improvements have solved the problems or bug s in the above examples, but they still violate the principle of not creating functions or closures in loops.

Not recommended

(function(w){
    'use strict';
    var numbers = [1, 2, 3],
        i;
    
    for(i = 0; i < numbers.length; i++) {
        (function(index, number){
            w.setTimeout(function() {
                w.alert('Index ' + index + ' with number ' + number);
            }, 0);
        }(i, numbers[i]));
    }
}(window));

The next improvements have solved the problem and followed the norms. However, you will find that it seems too complicated and tedious. There should be a better solution.

Incomplete recommendation

(function(w){
    'use strict';
    var numbers = [1, 2, 3],
        i;
    function alertIndexWithNumber(index, number) {
        return function() {
            w.alert('Index ' + index + ' with number ' + number);
        };
    }
    for(i = 0; i < numbers.length; i++) {
        w.setTimeout(alertIndexWithNumber(i, numbers[i]), 0);
    }
}(window));

The problem of converting a loop statement to the way a function is executed can be solved immediately, and each loop creates a closure correspondingly. Functional style is more recommendable and looks more natural and predictable.

Recommend

(function(w){
    'use strict';
    var numbers = [1, 2, 3];
    
    numbers.forEach(function(number, index) {
        w.setTimeout(function() {
            w.alert('Index ' + index + ' with number ' + number);
        }, 0);
    });
}(window));

eval function (devil)

eval() is not only confusing the context, but also dangerous. There will always be a better, clearer and safer way to write your code, so try not to use the eval() function.

this keyword

Use this keyword only in object constructors, methods, and set closures. The meaning of this is somewhat misleading here. It sometimes points to global objects (most of the time), sometimes to the domain of the caller (in eval), sometimes to a node in the DOM tree (when bound to html attributes with event processing), sometimes to a newly created object (in the constructor), and sometimes to other objects (if the function is called () and applied () to execute and invoke). When).

Because it is so easily mistaken, please limit its usage scenarios:

  • In the constructor
  • In the object's method (including the closure created therefrom)

Using ECMAScript5

It is recommended to use the new syntax and functions in ECMAScript5. This will simplify your program and make your code more flexible and reusable.

Iteration of attributes for arrays and objects

Iterative method of ECMAScript5 is used to iterate the array. Use Array.forEach, or if you want to interrupt iterations in a particular situation, use Array.every.

(function(){
    'use strict';
    
    [1, 2, 3, 4, 5].every(function(element, index, arr) {
        console.log(element + ' at index ' + index + ' in array ' + arr);
        
        if(index !== 5) {
            return true;
        }
    });
    
    var obj = {
        a: 'A',
        b: 'B',
        'c-d-e': 'CDE'
    };
    
    Object.keys(obj).forEach(function(element, index, arr) {
        console.log('Key ' + element + ' has value ' + obj[element]);
    });
}());

Array and object literals

Use arrays and object literals instead of arrays and object constructors. Array constructors are easy to make mistakes in their parameters.

Not recommended

var a1 = new Array(x1, x2, x3);
var a2 = new Array(x1, x2);
var a3 = new Array(x1);
var a4 = new Array();

Because of this, if you change code parameters from two to one, there is a high probability that the array will change unexpectedly in length. To avoid such weird situations, always use more readable array literals.

Recommend

var a = [x1, x2, x3];
var a2 = [x1, x2];
var a3 = [x1];
var a4 = [];

Object constructors don't have similar problems, but for readability and uniformity, we should use object literals.

Not recommended

var o = new Object();
var o2 = new Object();
o2.a = 0;
o2.b = 1;
o2.c = 2;
o2['strange key'] = 3;

Recommend

var o = {};
var o2 = {
    a: 0,
    b: 1,
    c: 2,
    'strange key': 3
};

Modifying the prototype chain of built-in objects

Modifying built-in items such as Object.prototype and Array.prototype is strictly prohibited. Modifying other built-in objects, such as Function.prototype, is not so harmful, but it will always lead to problems that are difficult to debug in the development process, which should also be avoided.

Custom toString() method

You can control object stringing by customizing toString(). That's good, but you have to make sure that your approach always succeeds without any other side effects. If your method fails to meet this standard, it will cause serious problems. If toString() calls a method, this method makes an assertion, when the assertion fails, it may output the name of the object it is in, of course, the object also needs to call toString().

Parentheses

Generally, parentheses are used cautiously when they are really needed in grammar and semantics. Do not use on unary operators, such as delete, typeof and void, or after keywords, such as return, throw, case, new, etc.

Character string

Unified use of single quotation marks ('), not double quotation marks ('). This is very helpful in creating html strings:

var msg = 'This is some HTML <div class="makes-sense"></div>';

Ternary Conditional Judgment (Fast Method of if)

Assign or return statements with ternary operators. Use in relatively simple situations, avoid using in complex situations. Nobody wants to use 10 lines of ternary operators to get their brains around.

Not recommended

if(x === 10) {
    return 'valid';
} else {
    return 'invalid';
}

Recommend

return x === 10 ? 'valid' : 'invalid';

Reference

Posted by culprit on Sun, 24 Mar 2019 06:09:30 -0700