I want to write an efficient javascript class library but I can't start;
Try to read other people's library, but understand it as if it is understood;
I plan to study advanced js functions well, but the content in the authoritative book is too scattered.
Even if you remember the "usage", you don't think about the "method" when you want to "use".
Maybe you, like me, seem to have an invisible force that restrains our plans, making us repeatedly think that the limitations of knowledge have made us stand still and difficult to move forward.
During this period, various homework, course design, and experimental reports have doubled. It is rare to squeeze out a little time, never sleep in, and sort out the books I have read in the past just to be closer to writing my own library.
This article is referenced from "Javascript Language Essence" and "Effective JavaScript". All the examples have been debugged. After understanding them, I want to make some "deep" principles a little simpler.
1. Variable scope
Scope is like oxygen to programmers. It's everywhere, and you don't even think about it. But when it is polluted (such as using global objects), you will feel choking (such as slow application response). JavaScript core scope rules are simple, carefully designed, and very powerful. Effective use of javascript requires mastering some of the basic concepts of variable scope and understanding some extreme situations that can lead to elusive, annoying problems.
1.1 Try to use global variables as little as possible
JavaScript is easy to create variables in a global namespace. Creating global variables is effortless because it does not require any form of declaration and can be accessed automatically by all code of the entire program.
For us beginners, when encountering certain needs (for example, when the transmitted data is recorded, waiting for a certain time to be called, or when a certain function is often used), we finally think of global functions. Even the C language process-oriented thinking I learned in my freshman year is too deeply rooted, and the system is neatly full of functions. Defining global variables can pollute shared public namespaces and can lead to unexpected naming conflicts. Global variables are also not conducive to modularity, as it causes unnecessary coupling between independent components in the program. Seriously speaking, too much global (including style sheets, directly defining styles of div or a) and integrating them into multiple people's developments will be a catastrophic mistake. This is why all jQuery code is wrapped in an instantly executed anonymous expression - self-calling anonymous functions. When the browser loads the jQuery file, it starts execution immediately after calling the anonymous function, initializing various modules of jQuery to avoid destroying and polluting global variables and affecting other codes.
The code copy is as follows:
(function(window,undefined){
var jQuery = ...
//...
window.jQuery = window.$ = jQuery;
})(window);
In addition, you may think that it is more convenient to "write how you first and organize it later", but excellent programmers will constantly pay attention to the structure of the program, continuously classify related functions, and separate irrelevant components, and these behaviors are part of the programming formula.
Since global namespaces are the only way for independent components in JavaScript programs to interact, the use of global naming controls is inevitable. Components or libraries have to define some global variables. For use by other parts of the program. Otherwise, it is best to use local variables.
The code copy is as follows:
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 to global objects that can be accessed in the program's global scope, which serves as the initial value of this keyword. In a 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 with var within the global scope, or add it to the global object. The advantage of using var declarations is that they can clearly express the impact of global variables in the program scope.
Given that references to bound global variables can cause runtime errors, clear and concise saving scopes will make it easier for code users to understand those global variables that the program declares.
Since the global object provides a dynamic response mechanism for the global environment, it can be used to query a running environment and detect which features are available on this platform.
eg.ES5 introduces a global JSON object to read and write JSON format data.
The code copy is as follows:
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. But the built-in implementations provided by the host environment are almost more suitable because they are written into the browser in C. Because they strictly check for accuracy and consistency according to certain standards and generally provide better performance than third-party implementations.
The basic operation of the data structure course design simulation strings required that the methods provided by the language itself could not be used. JavaScript implements the basic operations of arrays very well. If it is just for general learning needs, the idea of simulation methods provided by the language itself is good, but if you really invest in development, you don’t need to consider choosing to use JavaScript built-in methods as soon as possible.
1.2 Avoid using with
The with statement provides any "convenience" that makes your application unreliable and inefficient. We need to call a series of methods on a single object in turn. Using the with statement can easily avoid duplicate references to objects:
The code copy is as follows:
function status(info){
var widget = new Widget();
with(widget){
setBackground("blue");
setForeground("white");
setText("Status : "+info);
show();
}
}
It is also very tempting to use the with statement to "import" (import) variables from a module object.
The code copy is as follows:
function f(x,y){
with(Math){
return min(round(x),sqrt(y));//Abstract quote
}
}
In fact, JavaScript treats all variables the same. JavaScript starts from the innermost scope and looks for variables outward. The with language treats an object as if it represents a variable scope, so inside the with code block, variable searches start by searching for attributes of the given variable name. If the property is not found in this object, continue searching in the external scope. The reference to each external variable in the with block implicitly assumes that there is no attribute with the same name in the with object (and any of its prototype objects). Creating or modifying an object with or its prototype elsewhere in the program does not necessarily follow such assumptions. The javascript engine certainly does not read local code to get those local variables you are using. JavaScript scope can be represented as efficient internal data structures, and variable searches are very fast. However, since the with code block needs to search for the prototype chain of the object to find all variables in the with code, its running speed is much lower than that of the general code block.
Instead of the with language, it is simple to bind the object to a short variable name.
The code copy is as follows:
function status(info){
var w = new Widget();
w.setBackground("blue");
w.setForeground("white");
w.setText("Status : "+info);
w.show();
}
In other cases, the best way is to explicitly bind local variables to relevant properties.
The code copy is as follows:
function f(x,y){
var min = Math.min,
round = Math.round,
sqrt = Math.sqrt;
return min(round(x),sqrt(y));
}
1.3 Proficient in closure
There is a single concept to understand closures:
a) JavaScript allows you to reference variables defined outside the current function.
The code copy is as follows:
function makeSandwich(){
var magicIngredient = "peanut butter";
function make(filling){
return magicIngredient + " and " + filling;
}
return make("jelly");
}
makeSandwich();// "peanut butter and jelly"
b) Even if the external function has returned, the current function can still refer to the variable defined by the external function.
The code copy is as follows:
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"
JavaScriptd's function values contain more information than the code required to execute when they are called. Moreover, javascript function values also store internally the variables they may refer to defined in their enclosed scope. Those functions that track variables within the scope they cover are called closures.
The make function is a closure whose code refers to two external variables: magicIngredient and filling. Whenever the make function is called, its code can refer to these two variables because the closure stores these two variables.
A function can refer to any variable within its scope, including parameter and external function variables. We can use this to write more general sandwichMaker functions.
The code copy is as follows:
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 also the core of many idioms.
c) The 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 with access to these external variables can be updated.
The code copy is as follows:
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 containing three closures. These three closures are set, type and get properties, and they all share access to val variables, and the set closure updates the value of val. Then call get and type to view the updated results.
1.4 Understand variable declaration improvement
JavaScript supports this method scope (references to the variable foo will be bound to the scope that declares the closest to the foo variable), but does not support block-level scope (the scope defined by the variable is not the closest closed statement or code block).
Not understanding this feature will lead to some subtle bugs:
The code copy is as follows:
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 clumsy scope of naming function expressions
The code copy is as follows:
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 has completely different meanings. The official difference between anonymous functions and named function expressions is that the latter is bound to a variable with the same function name as its function name, which serves as a local variable of the function. This can be used to write recursive function expressions.
The code copy is as follows:
var f = function find(tree,key){
//...
return find(tree.left , key) ||
find(tree.right,key);
}
It is worth noting that the scope of the variable find is only in its own function. Unlike function declarations, named function expressions cannot be referenced externally through its internal function name.
The code copy is as follows:
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 inherits Object.prototype.constructor (i.e., the constructor of Oject), just like with statements, this scope will be affected by the dynamic changes of Object.prototype. The way to avoid object contaminating the scope of function expressions in the system is to avoid adding properties in the Object.prototype at any time to avoid using any local variables with the same name as the standard Object.prototype property.
Another disadvantage in the popular javascript engine is the promotion of the declaration of named function expressions.
The code copy is as follows:
var f = function g(){return 17;}
g(); //17 (in nonconformat environment)
Some javascript environments even use the two functions f and g as different objects, resulting in unnecessary memory allocation.
1.6 Beware of clumsy scope for local block functions
The code copy is as follows:
function f() {return "global" ; }
function test(x){
function f(){return "local";}
var result = [];
if(x){
result.push(f());
}
result.push(f());
result result result;
}
test(true); //["local","local"]
test(false); //["local"]
The code copy is as follows:
function f() {return "global" ; }
function test(x){
var result = [];
if(x){
function f(){return "local";}
result.push(f());
}
result.push(f());
result result result;
}
test(true); //["local","local"]
test(false); //["local"]
JavaScript has no block-level scope, so the scope of the internal function f should be the entire test function. Some javascript environments do, but not all javascript environments do. JavaScript implementations report such functions as errors in strict mode (programs in strict mode with local block function declarations will report as a syntax error), which helps detect non-portable code, giving more wise and possible semantics to local block function declarations for future standard versions. In this case, it is possible to consider declaring a local variable within the test function to point to the global function f.