In his new book (Javascript Patterns), Stoyan Stefanov introduces many techniques for writing high-quality code, such as avoiding using global variables and using a single var keyword, cyclic pre-storage length and so on.
This article not only considers code optimization, but also the code design stage, including writing API documents, review of colleagues, and using JSLint. These habits can help you write higher quality, easier to understand, and more maintainable code (so that your code will be proud of you after many years ).
Compile maintainable code
Fixing software bugs requires a lot of effort. Especially after the code has been released, the maintenance cost increases with the increase of time. When you discover a BUG, you can fix it immediately. At this time, your code is still hot, and you do not need to recall it, because it was just written. But when you do other tasks and almost forget the code, you need:
- Learn and understand questions again
- Understand how the code solves the problem
Another problem is that in a large project or company, it is often because the person who solves the BUG is not the person who produces the BUG, and is not the person who discovers the BUG. Therefore, reducing the time needed to understand the code is the most important issue. Whether the code is written by yourself or by other members of the team, because we all want to create new and interesting things, rather than maintaining old code.
Another common problem in development is that it usually takes more time to read code than to write code. Sometimes it may take you an entire afternoon to write your code. This code can work at the time, but with the development, other things have changed a lot. At this time, you need to review and edit the code yourself. For example:
- There are bugs not solved
- Added new features
- The program needs to run in the new environment (for example, a newly published browser)
- Code Problems
- The code needs to be rewritten because it modifies the architecture and even uses another language.
For these reasons, it may take weeks to read the code you wrote in the afternoon. Therefore, writing maintenance code is critical to the success of the software. The maintenance code includes:
- Readability
- Continuity
- Foresight
- It seems to be written by a person.
- Documentation available
Minimal global variables
Javascript uses functions to define the scope. A variable declared inside the function is invisible outside. Therefore, global variables are declared outside of any function or are not declared.
In Javascript, there is an accessible global object outside of any function. Every global variable you create is an attribute of this object. In a browser, Windows is usually used to refer to this global variable for convenience. The following code creates a global variable:
myglobal = "hello"; // antipatternconsole.log(myglobal); // "hello"console.log(window.myglobal); // "hello"console.log(window["myglobal"]); // "hello"console.log(this.myglobal); // "hello
Global Variables
The problem with global variables is that they are shared in all your code or on one page. They are under the same namespace, which usually results in a variable name conflict-two variables with the same name, but they are actually different in use.
Generally, some other people's code needs to be introduced in some pages, such:
- Third-party JS Library
- Script of advertising partner
- Third-party user behavior analysis or statistical scripts
- Different components and buttons
Add a third-party component to define a global variable: result. Then, a global variable result is defined in your program. The final result will overwrite the result before the vertex, so that the third-party scripts will stop working.
Therefore, to be friendly to other scripts, the fewer global variables on a page, the better. There will be some methods later to show you how to reduce global variables, such as using namespaces or self-executed anonymous functions, but the best way to avoid global variables is to use the var keyword to declare variables.
Because of the two features of javascript, it is very easy to create a global variable. First, you can use a variable that is not even declared. Second, in javascript, all undeclared variables will become an attribute of the Global Object (just like a declared global variable ). Let's take a look at this example:
function sum(x,y){ result = x + y; return result;}
In this Code, the result is used without being declared, and this Code can work well, but after calling this function, A global variable named result will be added, which is the root cause of all problems.
To solve this problem, use var:
function sum(x,y){ var result = x + y; return result;}
A bad habit is to use the chained method to assign values when declaring variables. At this time, a is a local variable, but B is a global variable.
function foo(){ var a=b=0; ....}
This is because the expression B = 0 is executed first and B is not declared during execution, so B becomes a global variable and then returns the value 0 of this expression, the declared variable a, in other words, is as if you input: var a = (B = 0 );
If you have declared a variable, the value assignment of this chain is no problem:
function foo(){ var a,b; ...}
Another reason to avoid using global variables is to consider program portability. If you want your code to work in different environments, then, using global variables is likely to conflict with the global variables in the new system (maybe there is no problem in the previous system ).
Forget the influence of var
There is another difference between the global variables declared using var and those generated without var:
The global variables created with the var statement cannot be deleted. Global variables that are not declared using var can be deleted. This indicates that the global variables generated without using var are not real variables, but they are only attributes of the global object. The attribute can be deleted through delete, but the variable cannot:
// define three globalsvar global_var = 1;global_novar = 2; // antipattern(function () { global_fromfunc = 3; // antipattern}()); // attempt to deletedelete global_var; // falsedelete global_novar; // truedelete global_fromfunc; // true // test the deletiontypeof global_var; // "number"typeof global_novar; // "undefined"typeof global_fromfunc; // "undefined"
In ES5's strict mode, an error is reported when a value is assigned to a declared variable.
Read Global Objects
In the browser, you can use the window variable to read Global Objects (unless you redefine the window object in the function ). However, in some environments, windows may not be used. You can use the following code to obtain global objects:
var global = (function(){ return this;})();
The reason for obtaining the global object is that this points to the global object within the function. However, this will not work in ES5's strict mode. You need to adapt to some other modes. When developing your own library, you can encapsulate your code in an immediate function and then pass this as a parameter.
Single var mode
At the top of your code, you only use the var keyword, which has the following benefits:
- All the required variables can be viewed in one place.
- Avoid using an undefined variable
- Helps you remember declared variables and reduce global variables
- Simpler code
Writing is simple:
function func() { var a = 1, b = 2, sum = a + b, myobject = {}, i, j; // function body...}
Multiple variables are declared using one var and comma. It is also good to assign default values to variables during declaration, which can avoid some logical errors and improve code readability. When you read the code, you can also guess the usage of the Variable Based on the default value of the variable.
You can also do some practical work when declaring variables, such as sum = a + B. In addition, when operating DOM elements, you can also save the reference of the DOM element in a variable:
function updateElement() { var el = document.getElementById("result"), style = el.style; // do something with el and style...}
Var misuse
JavaScript allows you to have multiple var statements in the function, but all of them are the same as declarations at the top of the function. This feature may cause some strange logic problems when you use a variable and declare it later. For JavaScript, variables are declared as long as they are in the same scope, even before the var statement. Let's take a look at this example:
myname = "global"; // global variablefunction func() { alert(myname); // "undefined" var myname = "local"; alert(myname); // "local"}func();
In this example, you may expect global to pop up for the first time and local to pop up for the second time. Because the var statement is not used to declare myname for the first time, this is the global variable myname. The second statement is clear, and then alert should be the local value. In fact, this is not the case. As long as you see var myname in the function, js considers that you declare this variable in this function, but when reading the value of this variable, the var statement has not been executed, so it is undefined. It is a strange logic. The above code is equivalent:
myname = "global"; // global variablefunction func() { var myname; // same as -> var myname = undefined; alert(myname); // "undefined" myname = "local"; alert(myname); // "local"}func();
Let's explain this phenomenon. In code parsing, there are two steps. The first step is to process the declaration of the variable function. This step is to process the context of the entire code. The second step is to create a function expression and undefined variables during code execution. In fact, we only assume that this concept is not in the ECMAScript norms, but this behavior is often explained in this way.
For Loop
In the for loop, you will iterate some array elements or some HTML elements. The for loop is often like this:
for (var i = 0; i < myarray.length; i++) { // do something with myarray[i]}
The problem with this writing is that the length of the array is calculated during each iteration, especially when this parameter is not an array but a set of HTML elements, it will reduce the performance of your program. The collection of HTML elements is on the page, so that each time the corresponding elements are searched on the page, this is very time-consuming. Therefore, for a for loop, you need to save the length of the array in advance and write it as follows:
for (var i = 0, max = myarray.length; i < max; i++) { // do something with myarray[i]}
In this way, the length of the parameter is cached, and you do not need to search for computation during each iteration. When searching for an HTML Element Set, the length of the cache parameter can significantly improve the performance. The speed under Safari is doubled, and the speed under IE7 is increased by 190 times. Note that when you need to modify the number of DOM elements, you certainly want this value to be updated at any time rather than a constant.
Using the single var mode below, you can also mention var outside the loop:
function looper() { var i = 0, max, myarray = []; // ... for (i = 0, max = myarray.length; i < max; i++) { // do something with myarray[i] }}
This mode can enhance the continuity of the entire code, but it is not easy to copy and paste the code when you refactor it. For example, if you want to use this loop in other functions, you need to confirm that I and max have been processed in the new function (you may need to delete this ).
This function has two points to optimize: one variable can be less (max is not required), and the number is reduced to 0. A number is faster than the number and the other number.
Therefore, you can write as follows:
var i, myarray = [];for (i = myarray.length; i--;) { // do something with myarray[i]}
For the second point:
var myarray = [], i = myarray.length;while (i--) { // do something with myarray[i]}
This is the optimization of two relatively small points. In addition, JSLint may have comments on I.
For-in Loop
The for-in loop is used to iterate non-array objects. The use of the for-in loop is usually also an enumeration.
Technically, you can use for-in to loop arrays, because arrays are also objects, but are not recommended. If the array has some custom extended functions, an error occurs. In addition, the order of object attributes is also unknown in the for-in loop. So it is best to use a normal loop to loop the array and use for-in to loop the object.
In the process of loop objects, it is important to use the hasOwnProperty () method to check whether the attributes of the object are the attributes of the prototype chain.
Let's take a look at the example below.
// the objectvar man = { hands: 2, legs: 2, heads: 1}; // somewhere else in the code// a method was added to all objectsif (typeof Object.prototype.clone === "undefined") { Object.prototype.clone = function () {};}
In this example, we have a simple object called man. Before or after other man definitions, the object prototype has a useful clone () method. Because of the prototype chain, all objects automatically obtain this method. To enable the clone method when enumerating man objects, you need to use the hasOwnProperty method. If there is no difference between the methods from the prototype chain, there will be some unexpected things:
// 1.// for-in loopfor (var i in man) { if (man.hasOwnProperty(i)) { // filter console.log(i, ":", man[i]); }}/* result in the consolehands : 2legs : 2heads : 1*/// 2.// antipattern:// for-in loop without checking hasOwnProperty()for (var i in man) { console.log(i, ":", man[i]);}/*result in the consolehands : 2legs : 2heads : 1clone: function()*/
Another method is as follows:
for (var i in man) { if (Object.prototype.hasOwnProperty.call(man, i)) { // filter console.log(i, ":", man[i]); }}
The advantage of this writing is that man can avoid conflicts caused by redefinition of the hasOwnProperty method. If you do not want to write such a long string, you can also:
var i, hasOwn = Object.prototype.hasOwnProperty;for (i in man) { if (hasOwn.call(man, i)) { // filter console.log(i, ":", man[i]); }}
Strictly speaking, not applying hasOwnProperty is not an error. Depending on the difficulty of the task and your confidence in the code, you can also skip this loop. But when you're not sure, you 'd better use this method to check.
In another format change (does not pass the jsLint check), remove the for braces, and then place if in the same row. The benefits of doing so can make the loop body more prominent, with fewer indentation:
// Warning: doesn't pass JSLintvar i, hasOwn = Object.prototype.hasOwnProperty;for (i in man) if (hasOwn.call(man, i)) { // filter console.log(i, ":", man[i]);}
Do not extend the built-in prototype
Extended prototype constructor can provide some powerful functions, but sometimes it is too powerful.
Sometimes you will expand the prototype methods of Object (), Array (), and Fucntion (), which will lead to maintainability problems, because it will make your code portability worse. When other developers use your code, they only need native methods and do not need additional functions.
In addition, the method you add will be traversed if the hasOwnProperty method is not used during the loop, which will be confusing.
Therefore, it is best not to extend basic objects. Unless it is the following:
- You are sure that in the future, according to The ECMAScript specification, the browser will add the corresponding prototype method. That's okay, but you just implemented this function in advance.
- You are sure that the method you want to implement does not exist-maybe it is implemented elsewhere in the Code, or some browsers support it.
- Have clear documents and communicate with team members
In these cases, you can add them, preferably in the following form:
if (typeof Object.prototype.myMethod !== "function") { Object.prototype.myMethod = function () { // implementation... };}
To be continued...