In the previous introduction, we already know that Javascript has no block-level function, only function-level scope.
The code copy is as follows:
function test() { // a scope
for(var i = 0; i < 10; i++) { // not a scope
// count
}
console.log(i); // 10
}
There is also no namespace displayed in Javascript, which means everything is defined in the global scope. Each time a variable is referenced, Javascript will traverse the entire global scope up until it is found. If the variable is still not found through the full global scope, a ReferenceError error is thrown.
Please enter a picture description
Implicit global variables
The code copy is as follows:
// script A
foo = '42';
// script B
var foo = '42'
The above two examples have different effects. The first will define the variable foo in the global scope, while the second will define the variable foo in the current scope.
We must note that if you do not use the keyword var, it will have unexpected impact.
The code copy is as follows:
// global scope
var foo = 42;
function test() {
// local scope
foo = 21;
}
test();
foo; // 21
Since var is not used to define the variable foo in the function test, the global variable foo outside the function will be overwritten. Although it doesn't seem like a big problem, if there are thousands of lines of code, it will be a difficult bug to track.
The code copy is as follows:
// global scope
var items = [/* some list */];
for(var i = 0; i < 10; i++) {
subLoop();
}
function subLoop() {
// scope of subLoop
for(i = 0; i < 10; i++) { // missing var statement
// do amazing stuff!
}
}
In the above example, the external loop will stop when the first execution is executed, because the variable i inside the subloop function will override the external global variable i. We only need to add a var inside the function to avoid this error, so we must not forget to add the keyword var when defining variables. Unless we do want to have an impact on external global variables.
Local variables
Local variables in Javascript can only be generated in two ways, one is declared through the keyword var, and the other is used as formal parameters of the function.
The code copy is as follows:
// global scope
var foo = 1;
var bar = 2;
var i = 2;
function test(i) {
// local scope of the function test
i = 5;
var foo = 3;
bar = 4;
}
test(10);
At this time, the variables i and foo inside the function test are local variables, and bar will override the external global variable bar.
Hoisting
Javascript will promote variable declarations, which means that both var expressions and function declarations will be promoted to the top of the scope.
The code copy is as follows:
bar();
var bar = function() {};
var someValue = 42;
test();
function test(data) {
if (false) {
goo = 1;
} else {
var goo = 2;
}
for(var i = 0; i < 100; i++) {
var e = data[i];
}
}
Before the above code is run, the declaration of var expression and function test will be promoted to the top, so the program will run normally and will not report an error.
The code copy is as follows:
// var statements got moved here
var bar, someValue; // default to 'undefined'
// the function declaration got moved up too
function test(data) {
var goo, i, e; // missing block scope moves these here
if (false) {
goo = 1;
} else {
goo = 2;
}
for(i = 0; i < 100; i++) {
e = data[i];
}
}
bar(); // fails with a TypeError since bar is still 'undefined'
someValue = 42; // assignments are not affected by hoisting
bar = function() {};
test();
Since Javascript does not have block-level scope, this will not only improve the var expression, but also make the if structure less intuitive.
In the above example, although it seems that if is operating on the global variable goo, in fact, since the variable goo is promoted, the local variable is modified.
If you don't have any understanding of the elevation rules, you may think that the following code will throw a ReferenceError error.
The code copy is as follows:
// check whether SomeImportantThing has been initialized
if (!SomeImportantThing) {
var SomeImportantThing = {};
}
Of course, the above code is not wrong, because the var expression has been promoted to the top before the code is run.
The code copy is as follows:
var SomeImportantThing;
// other code might initialize SomeImportantThing here, or not
// make sure it's there
if (!SomeImportantThing) {
SomeImportantThing = {};
}
Here I would like to recommend @nightire Fan Ge's blog post "Understanding JavaScript (II)", which explains the improvement very thoroughly.
Name resolution order
When trying to access a foo variable within a function scope, Javascript will look for it in the following order:
Whether there is a definition of var foo in the current scope.
Whether there is a foo variable in the function parameter.
Whether the function itself is foo.
Jump to the outer definition domain and start looking up from the first part.
Namespace
One of the most common problems is naming conflicts, because Javascript has only one global scope. But this problem can be solved by anonymous external functions.
The code copy is as follows:
(function() {
// a self contained "namespace"
window.foo = function() {
// an exposed closure
};
})(); // execute the function immediately
The anonymous functions in the above example are considered expressions, so they are executed.
The code copy is as follows:
( // evaluate the function inside the parentses
function() {}
) // and return the function object
() // call the result of the evaluation
Of course, we can also use other methods to call function expressions, different structures, but the same effect.
The code copy is as follows:
// A few other styles for directly invoking the
!function(){}()
+function(){}()
(function(){}());
// and so on...
Summarize
It is recommended that you use anonymous external functions to encapsulate the code into the space, which not only solves namespace conflicts, but also facilitates the modularization of the program.
In addition, using global variables is not a good habit, which will bring high maintenance costs and is prone to errors.
Namespaces have the same types, functions, variables, templates, etc., all belong to entities.
The main commonality of an entity is that it can have a name. (Also, a tag can also have a name, but it is not an entity.)
Namespace scope is a general term in scope, which is parallel to block scope, class scope, function prototype scope, and function scope (only valid for labels). The names declared within the namespace are in the namespace scope. Global names are considered in the implicit global namespace scope.
The function of a namespace is indeed scope, but it is different from a simple scope. You can declare the same namespace in multiple places in multiple times, but the content inside cannot be redefined. They will eventually synthesize a namespace, just like std, macro definitions everywhere.