The word "pit" means "trap". Due to the nature of JavaScript's "weak language", it is extremely loose and flexible during use, but it is also extremely easy to "be hit". These pits are often hidden, so you must keep your eyes open to smooth sailing on the road of learning and applying JS.
1. Global variables
JavaScript manages scopes through functions. Variables declared inside a function are only inside this function and are not available outside the function. On the other hand, global variables are declared outside any function or are used simply and simply without declaration.
"Use it simply without declaring" means that variables are declared without using the var keyword. We have already made it clear that the way to avoid implicitly generating global variables is to declare variables with the var keyword as much as possible.
But do you think it's OK to use var? Let’s take a look at this pit:
The code copy is as follows:
function foo() {
var a = b = 0;
// body...
}
Maybe what you expect is two local variables, but b is a real global variable. Why? Because the assignment operation is from right to left, so this is equivalent to:
The code copy is as follows:
function foo() {
var a = (b = 0);
// body...
}
So b is a global variable.
Fill in the pit: variable declarations, it is best to come one by one, don’t do wholesale~_~;
2. Variable declaration
Let’s take a look at the pit first:
The code copy is as follows:
myName = "global";
function foo() {
alert(myName);
var myName = "local";
alert(myName);
}
foo();
At first glance, we expect to expect the results of two alerts to be "global" and "local", respectively, but the real results are "undefined" and "local". Why? Because variables are declared in the same scope (same function), and are first parsed by the top of the scope.
So the execution behavior of the above code snippet might look like this:
The code copy is as follows:
function foo() {
var myName;
alert(myName); // "undefined"
myName = "local";
alert(myName); // "local"
}
Use another pit to test whether you really understand the pre-parsement:
The code copy is as follows:
if (!("a" in window)) {
var a = 1;
}
alert(a);
The declaration of a variable is advanced to the top of the code and has not been assigned yet. Next, enter the if statement, and judge that the "a" in window is established (a has been declared as a global variable), so the judgment statement calculation result is false, and the if statement will jump out, so the value of a is undefined.
The code copy is as follows:
var a; // "undefined"
console.log("a" in window); // true
if (!("a" in window)) {
var a = 1; // Not executed
}
alert(a); // "undefined"
Fill in the pit: It is best to manually place the variable declaration at the top of the scope. For variables that cannot be assigned at the moment, you can use the method of declaring first and then assigning values.
3. Function declaration
Function declarations are also advanced to the top of the scope, and are parsed and evaluated before any expressions and statements are parsed and evaluated.
The code copy is as follows:
alert(typeof foo); // "function"
function foo() {
// body...
}
You can compare it:
The code copy is as follows:
alert(typeof foo); // "undefined"
var foo = function () {
// body...
};
If you understand this principle, will you still fall into the following pitfalls?
The code copy is as follows:
function test() {
alert("1");
}
test();
function test() {
alert("2");
}
test();
When running the above code snippet, the pop-up windows you see are "2", why aren't they "1" and "2" respectively? It's very simple, the declaration of test is parsed before test(). Since the latter overrides the former, the result of both executions is "2".
Fill in the pit: In most cases, I use function expressions instead of function declarations, especially in some statement blocks.
4. Function expressions
Let’s look at the named function expression first. Of course, it must have a name, for example:
Copy the code as follows: var bar = function foo() {
// body...
};
It should be noted that the function name is only visible to its function inside. Such as the following pitfalls:
The code copy is as follows:
var bar = function foo() {
foo(); // Run normally
};
foo(); // Error: ReferenceError
Fill in the pit: Try to use named function expressions as little as possible (except for some recursion and debugging purposes), and never use function names externally.
5. Function self-execution
For function expressions, you can execute by adding () afterwards, and parameters can be passed in parentheses, while the function declaration cannot. pit:
The code copy is as follows:
// (1) This is just a grouping operator, not a function call!
// So the function here has not been executed, it is still a statement
function foo(x) {
alert(x);
}(1);
The following code snippets are executed separately, and the pop-up window shows "1". Because before (1), they are function expressions, so the () here is not a grouping operator, but an operator, indicating the call execution.
Copy the code as follows://standard anonymous function expression
var bar = function foo(x) {
alert(x);
}(1);
// The previous() converts the function declaration into an expression
(function foo(x) {
alert(x);
})(1);
// The entire () is an expression
(function foo(x) {
alert(x);
}(1));
// new expression
new function foo(x) {
alert(x);
}(1);
// &&, ||, !, +, -, ~ etc. operators (and commas) to disambiguate function expressions and function declarations
// So once the parser knows that one of them is an expression, the others will also default to expressions.
true && function foo(x) {
alert(x);
}(1);
Fill the pit: The key to this pit is to figure out the essence of all kinds of function expressions.
6. Closure in the loop
The following demonstrates a common pitfall:
The code copy is as follows:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<h3>when clicking links below, show the number of its sequence</h3>
<ul>
<li><a href="#">link #0</a></li>
<li><a href="#">link #1</a></li>
<li><a href="#">link #2</a></li>
<li><a href="#">link #3</a></li>
<li><a href="#">link #4</a></li>
</ul>
</body>
</html>
The code copy is as follows:
var links = document.getElementsByTagName("ul")[0].getElementsByTagName("a");
for (var i = 0, l = links.length; i < l; i++) {
links[i].onclick = function (e) {
e.preventDefault();
alert("You click link #" + i);
}
}
We expect that when clicking on the i-th link, we get the value of the index i in this sequence. However, no matter which link is clicked, we get the final result of i after the loop: "5".
Explain the reason: When alert is called, the anonymous function expression in the for loop maintains a reference to the external variable i (closure). At this time, the loop has ended and the value of i is modified to "5".
Fill in the pit: In order to get the desired result, you need to create a copy of the variable i in each loop. The following demonstrates the correct approach:
The code copy is as follows:
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<h3>when clicking links below, show the number of its sequence</h3>
<ul>
<li><a href="#">link #0</a></li>
<li><a href="#">link #1</a></li>
<li><a href="#">link #2</a></li>
<li><a href="#">link #3</a></li>
<li><a href="#">link #4</a></li>
</ul>
</body>
</html>
The code copy is as follows:
var links = document.getElementsByTagName("ul")[0].getElementsByTagName("a");
for (var i = 0, l = links.length; i < l; i++) {
links[i].onclick = (function (index) {
return function (e) {
e.preventDefault();
alert("You click link #" + index);
}
})(i);
}
As you can see, the form of (function () { ... })() is the self-execution of the function mentioned above. i is passed to index as a parameter. When alert executes again, it has a reference to index. At this time, this value will not be changed by the loop. Of course, after understanding the principle, you can also write like this:
The code copy is as follows:
for (var i = 0, l = links.length; i < l; i++) {
(function (index) {
links[i].onclick = function (e) {
e.preventDefault();
alert("You click link #" + index);
}
})(i);
}
It works too.