How do you write high quality JS code

  • 2020-05-09 18:10:10
  • OfStack

There is no way to write an efficient javascript library;

Try to read other people's libraries, but understand as if they understand;

I tried to dig into js high-level functions, but the authoritative books were too fragmented,

Even if you remember the "usage", you don't think about the "method" when you want to "use".

Perhaps you and I, like 1, seem to have an invisible force constraining our plans, let us 1 think the limits of knowledge, causing us to stay in the same place, it is difficult to move forward.

During this period, the pressure of all kinds of assignments, course designs and lab reports is increasing. Rare squeeze out a little bit of time, never sleep late, sorting and summarizing the books of the past, only in order to be able to write their own library near 1 point.

This paper refers to javascript language essence and Effective JavaScript. Examples have been debugged, after understanding, I want to 1 some "deep" truth to say a little bit more simple.

1. Variable scope

Scopes are like oxygen to programmers. It is so ubiquitous that you often don't even think about it. But when it is contaminated (for example by using global objects), you feel suffocated (for example if the application is slow to respond). The javascript core scope rules are simple, well-designed, and powerful. Effective use of javascript requires a grasp of some basic concepts of variable scope and an understanding of extreme situations that can lead to elusive and annoying problems.

1.1 minimize the use of global variables

javascript makes it easy to create variables in the global namespace. Creating a global variable is effortless because it does not require any form of declaration and can be accessed automatically by all the code in the entire program.

For those of us who are beginners, there are certain requirements (for example, that the data being transmitted be recorded and used while waiting for a function to be called at a certain time; Or when a function is frequently used), don't hesitate to think of global functions, or even the process-oriented C language you learned in college 1 is so ingrained that the system is full of functions. Defining global variables pollutes Shared common namespaces and can lead to unexpected naming conflicts. Global variables are also bad for modularity because they lead to unnecessary coupling between independent components in a program. Seriously, too much globalism (including style sheets that directly define div or a styles) and integration into multiple developers would be a disastrous mistake. This is why all of jQuery's code is wrapped in an anonymous expression that is executed immediately -- an anonymous function that calls itself. Once the browser has loaded the jQuery file, the execution begins immediately after the anonymous function is called, initializing the various modules of jQuery, so as to avoid damaging and polluting the global variables so as to affect other code.


(function(window,undefined){
    var jQuery = ...
    //...
    window.jQuery = window.$ = jQuery;
})(window);

In addition, you might think it would be convenient to write something first and sort it out later, but good programmers keep an eye on the structure of the program, constantly categorizing related functions and separating unrelated components as part of their programming repertoire.

  because the global namespace is the only way for the individual components of an javascript program to interact through the lines, the use of globally named controls is inevitable. A component or library has to define some global variables. For use by other parts of the program. Otherwise, it is best to use local variables.


this.foo ;//undefined
foo = " global foo";
this.foo ;//"global foo"
var foo = "global foo";
this.foo = "changed";
foo ;//changed

The global namespace of javascript is also exposed as a global object accessible in the program's global scope, which is the initial value of the this keyword. In the web browser, the global object is bound to the global window variable. This means that there are two ways to create a global variable: declare it in the global scope using var, or add it to the global object. The benefit of using the var declaration is the ability to clearly express the effects of global variables at the program scope.

Since references to bound global variables can cause runtime errors, keeping the scope clear and concise makes it easier for the consumer of the code to understand that the program has declared those global variables.

Because the global object provides a dynamic response mechanism for the global environment, you can use it to query a runtime environment to see what features are available on that platform.

eg.ES5 introduces a global JSON object to read and write data in JSON format.


if(!this.JSON){
   this.JSON = {
         parse : ..,
         stringify : ...   
    } 
}

  if you provide an implementation of JSON, you can, of course, simply and unconditionally use your own implementation. However, built-in implementations provided by the hosting environment are almost more appropriate because they are written into the browser in the C language. Because they are rigorously checked for correctness and 1 uniqueness according to the criteria set by 1, they generally provide better performance than third party implementations.

The basic operation of the simulated string was designed for the data structure course, which required that the method provided by the language itself should not be used. javascript does a good job of basic manipulation of arrays, and the idea of a method provided by the simulation language itself is good if it's just a 1-like learning requirement, but if you're really into development, you don't have to worry about the first time you choose to use the javascript built-in method.

1.2 avoid with

The with statement provides any "convenience" that makes your application unreliable and inefficient. We need to call the 1 series of methods on the single 1 object in turn. Using the with statement makes it easy to avoid duplicate references to objects:


function status(info){
    var widget = new Widget();
    with(widget){
           setBackground("blue");
           setForeground("white");
           setText("Status : "+info);
           show();
    } 
}

It is also tempting to "import" (import) variables from module objects using the with statement.


function f(x,y){
   with(Math){
         return min(round(x),sqrt(y));// Abstract reference
   }
}

In fact, javascript treats all variables the same. javascript starts at the innermost scope and looks out for variables. The with language treats an object as if it represented a variable scope, so inside the with code block, the variable lookup begins by searching for the properties of a given variable name. If the property is not found in the object, the search continues in the external scope. The reference to each external variable in the with block implicitly assumes that there are no properties of the same name in the with object (and any of its prototype objects). Creating or modifying an with object or its prototype object elsewhere in the program will certainly follow this assumption. The javascript engine certainly doesn't read the local code to get what local variables you're using. The javascript scope can be represented as an efficient internal data structure, and variable lookup can be very fast. But because the with block requires a prototype chain of objects to search for all the variables in the with code, it runs much slower than the 1-like block.

Instead of the with language, it is simple to bind the object to a short variable name.


function status(info){
    var w = new Widget();
   
     w.setBackground("blue");
     w.setForeground("white");
     w.setText("Status : "+info);
     w.show();
  
}

In other cases, the best approach is to explicitly bind local variables to the relevant properties.


function f(x,y){
    var    min    = Math.min,
           round  = Math.round,
           sqrt   = Math.sqrt; 
     return min(round(x),sqrt(y));
}

1.3 master closures

Understanding closures has a single concept:

a) javascript allows you to refer to variables defined outside the current function.


function makeSandwich(){
   var magicIngredient = "peanut butter";
   function make(filling){
        return magicIngredient + " and " + filling;
   }
   return make("jelly"); 
}
makeSandwich();// "peanut butter and jelly"

b) the current function can still refer to the variables defined in the external function even if the external function has returned


function makeSandwich(){
   var magicIngredient = "peanut butter";
   function make(filling){
        return magicIngredient + " and " + filling;
   }
   return make; 
}
var f = sandwichMaker();
f("jelly");                      // "peanut butter and jelly"
f("bananas");               // "peanut butter and bananas"
f("mallows");               // "peanut butter and mallows"

The values of javascriptd's functions contain more information than the code needed to execute when they are called. Furthermore, the javascript function values internally store variables that they may reference that are defined in their enclosing scope. Functions that trace variables within the scope they cover are called closures.

The   make function is a closure whose code references two external variables: magicIngredient and filling. Whenever the make function is called, its code can reference these two variables because the closure stores them.

A function can reference any variable within its scope, including parameters and external function variables. We can use this 1 point to write a more generic sandwichMaker function.


function makeSandwich(magicIngredient){
   function make(filling){
        return magicIngredient + " and " + filling;
   }
   return make; 
}
var f = sandwichMaker( " ham " );
f("cheese");                      // "ham and cheese"
f("mustard");               // "ham and mustard"

Closures are one of the most elegant and expressive features of javascript and are at the heart of many idioms.

The c) closure can update the value of an external variable. In fact, closures store references to external variables, not copies of their values. Therefore, any closure that has access to these external variables can be updated.


function box(){
    var val = undefined;
    return {
         set : function(newval) {val = newval;},
         get : function (){return val;},
         type : function(){return typeof val;}
    };
}
var b = box();
b.type(); //undefined
b.set(98.6);
b.get();//98.6
b.type();//number

This example produces an object with three closures. The three closures are the set, type, and get properties, all of which share access to the val variable, and the set closure updates the val value. get and type are then called to see the updated results.

1.4 understand variable declaration promotion

javascript supports this scope (references to the variable foo are bound to the scope that declares the closest foo variable), but not block-level scope (the scope defined by a variable is not the closest enclosing statement or block of code).

Not understanding this feature will result in 1 of the subtle bug:


function isWinner(player,others){
    var highest = 0;
    for(var i = 0,n = others.length ;i<n;i++){
          var player = others[i];
          if(player.score > highest){
                   highest = player.score;
          }
    }
    return player.score > highest;
}

1.5 beware of the clumsy scope of named function expressions


function double(x){ return x*2; }
var f = function(x){ return x*2; }

The same piece of function code can also be used as an expression, but it has a completely different meaning. The official difference between an anonymous function and a named function expression is that the latter is bound to a variable with the same name as the function, which ACTS as a local variable of the function. This can be used to write recursive function expressions.


var f = function find(tree,key){
  //....
  return find(tree.left , key) ||
             find(tree.right,key);   
}

It is important to note that the scope of the variable find is only within its own functions, and unlike function declarations, named function expressions cannot be referenced externally by their internal function names.


find(myTree,"foo");//error : find is not defined;
var constructor = function(){ return null; }
var f= function(){
    return constructor();
};
f();//{}(in ES3 environments)

The program looks like it will produce null, but it will actually produce a new object.

Because the named function variable scope inherits Object.prototype.constructor (the constructor of Oject), like with statement 1, this scope is affected by dynamic changes in Object.prototype. The way to avoid object tainting the scope of function expressions in your system is to avoid adding properties to Object.prototype at any time, to avoid using any local variables with the same name as the standard Object.prototype property.

Another disadvantage of       in the popular javascript engine is the improved declaration of named function expressions.


var f = function g(){return 17;}
g(); //17 (in nonconformat environment)

Some javascript environments even treat the two functions f and g as separate objects, resulting in unnecessary memory allocation.

1.6 beware of clumsy scopes for local block function declarations


function f() {return "global" ; }
function  test(x){
    function f(){return "local";}
    var result = [];
    if(x){
         result.push(f());
    }     
     result.push(f());
     result result;
}
test(true);   //["local","local"]
test(false);  //["local"]


function f() {return "global" ; }
function  test(x){
    var result = [];
    if(x){
         function f(){return "local";}
         result.push(f());
    }     
     result.push(f());
     result result;
}
test(true);   //["local","local"]
test(false);  //["local"]

javascript has no block-level scope, so the scope of the inner function f should be the entire test function. 1 javascript environment that's true, but not all javascript environment so javascript implementations in strict mode to this kind of function for error report (with local block function declarations under strict mode the program will report into one grammatical errors), helps detect non-portable code, standard version for the future is in to the local block function declaration can give more sensible and semantics. In this case, consider declaring a local variable to refer to the global function f within the test function.


Related articles: