Chapter 6 functions and closures

achievements

chapter Code quantity (line)
6.1 0
6.2 14
6.3 44
6.4 154
Chapter 6 212

6.1 function declaration statement and anonymous function expression

You can declare functions through function declaration statements and anonymous function expressions.

6.2 classification of function calls

Table 6.1 classification of function calls

name explain
Method call Call the function through the receiver object (including apply and call calls)
constructor call Call the function through the new expression
function call Function calls other than the above two methods

The function used in the way of method call is called method. Similarly, the function used in the way of constructor call is called constructor.

Post of function declaration statement

A function declared by a function declaration statement can be called before the declared line of code. Although this example is done in the function
Within the scope, but the same is true for the global scope.

function hzh1() {
    hzh2(); // Call the function fn before declaring it
    function hzh2() {
        console.log('Huang Zihan');
    }
}
hzh1();
[Running] node "e:\HMV\JavaScript\JavaScript.js"
Huang Zihan

[Done] exited with code=0 in 99.724 seconds

A function declared by a function declaration statement can be called before the declared line of code. In the case of anonymous function expressions, the results will be different.

function hzh3() {
    hzh4();
    var hzh4 = function() {
        console.log('Huang Zihan');
    }
}
hzh3();
[Running] node "e:\HMV\JavaScript\JavaScript.js"
e:\HMV\JavaScript\JavaScript.js:2
    hzh4();
    ^

TypeError: hzh4 is not a function
    at hzh3 (e:\HMV\JavaScript\JavaScript.js:2:5)
    at Object.<anonymous> (e:\HMV\JavaScript\JavaScript.js:7:1)
    at Module._compile (internal/modules/cjs/loader.js:999:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1027:10)
    at Module.load (internal/modules/cjs/loader.js:863:32)
    at Function.Module._load (internal/modules/cjs/loader.js:708:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:60:12)
    at internal/main/run_main_module.js:17:47

[Done] exited with code=1 in 3.35 seconds

6.3 parameters and local variables

6.3.1 arguments object

Arguments can be accessed by using the arguments object within the function. The usage is shown in code listing 6.1.

Listing 6.1 shows an example of using the arguments object
function hzh1() {
    console.log(arguments.length);
    console.log(arguments[0], arguments[1], arguments[2]);
}
console.log("arguments.length Is the number of arguments, and the value is 1");
console.log("arguments[0]The value of is 7");
console.log(hzh1(7));
console.log("*********************************************");
console.log("arguments.length Is the number of arguments, and the value is 2");
console.log("arguments[0] The value of is 7, arguments[1] The value of is 8");
console.log(hzh1(7, 8));
console.log("*********************************************");
console.log("arguments.length Is the number of arguments, and the value is 1");
console.log("arguments[0]The value of is 7");
console.log("arguments[1]The value of is 8");
console.log("arguments[2] The value of is 9");
console.log(hzh1(7, 8, 9));
[Running] node "e:\HMV\JavaScript\JavaScript.js"
arguments.length Is the number of arguments, and the value is 1
arguments[0]The value of is 7
1
7 undefined undefined
undefined
*********************************************
arguments.length Is the number of arguments, and the value is 2
arguments[0] The value of is 7, arguments[1] The value of is 8
2
7 8 undefined
undefined
*********************************************
arguments.length Is the number of arguments, and the value is 1
arguments[0]The value of is 7
arguments[1]The value of is 8
arguments[2] The value of is 9
3
7 8 9
undefined

[Done] exited with code=0 in 0.506 seconds

Arguments without corresponding formal parameters can also be accessed through arguments. Because it can pass arguments Length knows the number of arguments, so you can write the so-called variable length parameter Function. The number of formal parameters can be obtained through the length attribute of the Function object itself.

Although arguments can be used as an array, it is not an array object in itself. Therefore, the methods in the array class cannot be used on it.

6.3.2 recursive function

A recursive function is a function that calls itself within a function. This method is called recursive execution or recursive call.

Code listing 6.2 factorial of n (example of recursive function)
function hzh1(hzh2) {
    if(hzh2 <= 1) {
        return 1;
    } else {
        return hzh2 * hzh1(hzh2 - 1); 
    }
}
console.log("Call function hzh1: ");
console.log(hzh1(5));
[Running] node "e:\HMV\JavaScript\JavaScript.js"
Call function hzh1: 
120

[Done] exited with code=0 in 1.416 seconds

If the recursive function keeps calling itself, the operation will not terminate (this is the same as the case of infinite loop, so it is commonly known as infinite recursion). The reaction of JavaScript after infinite recursion depends on the actual running environment. If it is the shell of SpiderMonkey, InternalError will occur as follows. In Rhino attached to Java 6, after infinite recursion, Java Lang. outofmemoryerror to stop Rhino.

// Infinite recursion in SpiderMonkey

function hzh() {
    hzh();
}
console.log("call hzh Function:");
console.log(hzh());
[Running] node "e:\HMV\JavaScript\JavaScript.js"
call hzh Function:
e:\HMV\JavaScript\JavaScript.js:3
function hzh() {
            ^

RangeError: Maximum call stack size exceeded
    at hzh (e:\HMV\JavaScript\JavaScript.js:3:13)
    at hzh (e:\HMV\JavaScript\JavaScript.js:4:5)
    at hzh (e:\HMV\JavaScript\JavaScript.js:4:5)
    at hzh (e:\HMV\JavaScript\JavaScript.js:4:5)
    at hzh (e:\HMV\JavaScript\JavaScript.js:4:5)
    at hzh (e:\HMV\JavaScript\JavaScript.js:4:5)
    at hzh (e:\HMV\JavaScript\JavaScript.js:4:5)
    at hzh (e:\HMV\JavaScript\JavaScript.js:4:5)
    at hzh (e:\HMV\JavaScript\JavaScript.js:4:5)
    at hzh (e:\HMV\JavaScript\JavaScript.js:4:5)

[Done] exited with code=1 in 0.225 seconds

The stop condition judgment of recursive execution must be set inside the recursive function, which is called termination condition. In the case of code listing 6.2, whether the value of parameter n is less than or equal to 1 will be judged at the beginning of the function. The code of the termination condition does not have to be written in the head of the recursive function, but in general, it is easier to read.

The processing that can be realized through loop must also be realized through recursive processing, and vice versa. This is because the essence of recursive call and loop processing is to perform an operation repeatedly. In most cases, the code implemented through loops will be more concise. Moreover, the execution efficiency of recursive processing in JavaScript is not necessarily very high. Therefore, it is generally best to avoid using recursion in JavaScript.

Can pass arguments Callee to get the reference of the Function object being executed. This reference can be used when implementing recursive functions through unnamed functions (so-called anonymous functions). The following is a specific example of calculating the factorial of n (note that arguments.callee is prohibited in the static mode of ECMAScript version 5).

// Factorial of n (using arguments.callee)
var hzh = (function(n) {
    if (n <= 1) { 
        return 1; 
    }
    else {
        return n*arguments.callee(n - 1);
    }
})(5);
console.log("output hzh Value of:");
console.log(hzh);
[Running] node "e:\HMV\JavaScript\JavaScript.js"
output hzh Value of:
120

[Done] exited with code=0 in 0.177 seconds

6.4 scope

Scope refers to the valid range of names (variable name and function name).

There are two scopes in JavaScript.

  • global scope
  • Function scope

The most out of scope (global) function is the code. The name declared outside the function belongs to the global scope. These names are called global variables and global functions.

Names declared inside a function have a function scope, and they are valid only inside the function. Relative to the global scope, it can be called local scope; Relative to global variables, they can also be called local variables. Parameter variables as function parameters also belong to function scope.

The function scope mechanism of JavaScript is subtly different from the local scope in Java (and many other programming languages). In Java, the scope of a local variable starts from the line that declares the variable in the method; In JavaScript, the scope of a function has nothing to do with the number of lines declared.

Take a look at the example in listing 6.3.

Code listing 6.3 considerations for function scope

var hzh1 = 1;
function hzh() {
    // Access variable x
    console.log('hzh1 = ' + hzh1);
    var hzh2 = 2;
    // Access variable x
    console.log('hzh2 = ' + hzh2);
}
hzh();
[Running] node "e:\HMV\JavaScript\JavaScript.js"
hzh1 = 1
hzh2 = 2

[Done] exited with code=0 in 0.311 seconds

[comment] the result here is different from that in the book. Please mark it.

At first glance, you'll think of the first console in the function Hzh Log () shows the global variable hzh1. However, hzh1 here is the local variable hzh1 declared on the next line. This is because the scope of the local variable hzh1 is within the whole function Hzh. Since it has not been assigned at this time, the value of variable hzh1 is undefined. That is, the function Hzh is equivalent to the following code.

function HZH() {
    var hzh3;
    console.log('hzh3 = ' + hzh3);
    hzh3 = 3;
    console.log('hzh3 = ' + hzh3);
}
HZH();
[Running] node "e:\HMV\JavaScript\JavaScript.js"
hzh3 = undefined
hzh3 = 3

[Done] exited with code=0 in 0.551 seconds

[comment] the experimental results here are different from those in the book. Mark it and the notes are the original text in the book.

The code in listing 6.3 is very difficult to understand and is often the cause of errors. Therefore, we recommend declaring all local variables at the beginning of the function.

Languages such as Java suggest that a variable should not be declared until it is used, but JavaScript is different. Please pay attention to this.

6.4.1 browser and scope

In client-side JavaScript, each window (label) and framework (including iframe) has its own global scope. The names in their global scope cannot be accessed between windows, but the parent and its framework can access each other.

6.4.2 block level scope

There is no concept of block level scope in JavaScript (ECMAScript), which is different from many other programming languages.. For example, see code listing 6.1. If the block level scope is considered to exist, the second console will be considered The result of log() should be 1, but the actual output is 2.

Code listing 6.1 misunderstandings about block level scopes
var hzh1 = 1;                  // global variable
{
    var hzh1 = 2;
    console.log("Output block level scope hzh1:  ");
    console.log('hzh1 = ' + hzh1);
}
console.log("Output global variable hzh1: ");
console.log('hzh1 = ' + hzh1); // Think the result will be 1?
[Running] node "e:\HMV\JavaScript\JavaScript.js"
Output block level scope hzh1:  
hzh1 = 2
 Output global variable hzh1: 
hzh1 = 2

[Done] exited with code=0 in 0.197 seconds

In listing 6.1, it seems that the variable hzh1 in the block level scope is redeclared in the code block, but in fact, it only assigns the global variable hzh1 to 2. In other words, this is equivalent to the following code.

var hzh3 = 1; // global variable
{
    hzh3 = 2;
    console.log("Output block level scope hzh3: ");
    console.log('hzh3 = ' + hzh3);
}
console.log("Output global variable hzh3: ");
console.log('hzh3 = ' + hzh3);
[Running] node "e:\HMV\JavaScript\JavaScript.js"
Output block level scope hzh3: 
hzh3 = 2
 Output global variable hzh3: 
hzh3 = 2

[Done] exited with code=0 in 0.205 seconds

This misunderstanding of block level scope also exists in function scope. It is customary to declare a loop variable in a for statement, but the scope of the loop variable is not limited to the for statement. In the following code, the local variable i is actually recycled.

function hzh() {
    var hzh4 = 4;
    for(var hzh4 = 0; hzh4 < 10; hzh4++) {
        console.log("ellipsis");
    }
    console.log('hzh4 = ' + hzh4);
}
hzh();
[Running] node "e:\HMV\JavaScript\JavaScript.js"
ellipsis
 ellipsis
 ellipsis
 ellipsis
 ellipsis
 ellipsis
 ellipsis
 ellipsis
 ellipsis
 ellipsis
hzh4 = 10

[Done] exited with code=0 in 0.188 seconds

6.4.3 let and block level scopes

Although there is no block level scope in ECMAScript version 5, JavaScript comes with the enhancement of let, which can achieve the effect of block level scope. You can use the let function in three ways: let definition (let declaration), let statement, and let expression. Although the grammatical structure is different, the principle is the same.

Let definitions (let declarations) are used in the same way as var declarations. Variables can be declared through the following syntax structure.

let var1 [= value1] [, var2 [= value2]] [, ..., varN [= valueN]];

Variables declared through let declarations have block level scope. Except for scope, it is no different from variables declared through var in other aspects. A simple example is shown in listing 6.4.

Code listing 6.4 let declaration
function hzh() {
    let hzh1 = 1;
    console.log("Output scope function hzh1: ")
    console.log('hzh1 = ' + hzh1);     // Output 1
    {
        let hzh1 = 2;
        console.log("Output at block level scope hzh1: ");
        console.log('hzh1 = ' + hzh1); // Output 2
    }                                  // The scope of let hzh1 = 2 ends here
    console.log("Output in function scope hzh1: ");
    console.log('hzh1 = ' + hzh1);     // Output 1
} 
hzh();
[Running] node "e:\HMV\JavaScript\JavaScript.js"
Output in function scope hzh1: 
hzh1 = 1
 Output at block level scope hzh1: 
hzh1 = 2
 Output in function scope hzh1: 
hzh1 = 1

[Done] exited with code=0 in 0.18 seconds

Regardless of the scope, let variables (variables declared through let declarations) are executed in a very similar way to var variables. See the comments section in listing 6.5.

Listing 6.5 shows a specific example of how the let variable is executed
// Name lookup
function HZH1() {
    let hzh1 = 1;
    {
        console.log("Output in block level scope hzh1: ");
        console.log('hzh1 = ' + hzh1); // Output 1. The code block is searched for names from the inside out
    }
}
HZH1();
console.log("****************************************************");
// This name is also valid until the let declaration is made
function HZH2() {
    let hzh2 = 2;
    {
        // Here let hzh2 = 2 scope.
        //However, since it has not been assigned a value, the let variable hzh2 has a value of undefined
        console.log("First output in block level scope hzh2: ");
        console.log('hzh2 = ' + hzh2);

        let hzh2 = 3;
        console.log("Second output in block level scope hzh2: ");
        console.log('hzh2 = ' + hzh2); // Output 2
    } 
}
HZH2();
[Running] node "e:\HMV\JavaScript\JavaScript.js"
Output in block level scope hzh1: 
hzh1 = 1
****************************************************
First output in block level scope hzh2: 
e:\HMV\JavaScript\JavaScript.js:18
        console.log('hzh2 = ' + hzh2);
                                ^

ReferenceError: Cannot access 'hzh2' before initialization
    at HZH2 (e:\HMV\JavaScript\JavaScript.js:18:33)
    at Object.<anonymous> (e:\HMV\JavaScript\JavaScript.js:25:1)
    at Module._compile (internal/modules/cjs/loader.js:999:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1027:10)
    at Module.load (internal/modules/cjs/loader.js:863:32)
    at Function.Module._load (internal/modules/cjs/loader.js:708:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:60:12)
    at internal/main/run_main_module.js:17:47

[Done] exited with code=1 in 0.203 seconds

[comment] the experimental results here are different from those in the book. Please mark them.

As follows, after changing the var declaration in the initialization expression of the for statement to the let variable, the scope will be limited to the for statement. This approach is more in line with the usual way of thinking. The same is true for the for in statement and for each in statement.

var arr = ["h", "z", "h"];
for(let hzh = 0, len = arr.length; hzh < len; hzh++) {
    console.log("arr[" + hzh + "] = " + arr[hzh]);
}
// This is outside the scope of let variable hzh
[Running] node "e:\HMV\JavaScript\JavaScript.js"
arr[0] = h
arr[1] = z
arr[2] = h

[Done] exited with code=0 in 0.259 seconds

The syntax structure of the let statement is as follows. The scope of the let variable is limited to the inside of the statement.

let (var1 [= value1] [, var2 [= value2] [, ..., varN [= valueN]]]) sentence;

The following is a specific example of a let statement.

let hzh = 1;
{                     // Code block
    console.log("Output in code block hzh: ");
    console.log("hzh = " + hzh); // Output 1
}                     // The scope of the let variable ends here
[Running] node "e:\HMV\JavaScript\JavaScript.js"
Output in code block hzh: 
hzh = 1

[Done] exited with code=0 in 0.185 seconds

Listing 6.6 is a concrete example of mixing var declarations with let statements.

Code listing 6.6 var declaration and let statement
function huangzihan() {
    var hzh1 = 1;
    let hzh2 = 2;
    {
        console.log("output hzh2: ");
        console.log("hzh2 = " + hzh2);     // Output 2
        console.log("");
        hzh3 = 3;
        console.log("output hzh3: ");
        console.log("hzh3 = " + hzh3);     // Output 3
        console.log("");
    }
    console.log("output hzh1: ");  // Output 1
    console.log("hzh1 = " + hzh1);
}

huangzihan();
[Running] node "e:\HMV\JavaScript\JavaScript.js"
output hzh2: 
hzh2 = 2

output hzh3: 
hzh3 = 3

output hzh1: 
hzh1 = 1

[Done] exited with code=0 in 0.264 seconds

Inside the let statement, declaring a variable with the same name as the let variable can cause TypeError problems. Here is an example.

// A variable with the same name cannot be declared through a let

let (hzh1 = 1) {
    let hzh1 = 2;
    console.log(hzh1); 
}

// You cannot declare a variable with the same name through var

let (hzh2 = 1) {
    var hzh2 = 2;
    console.log(hzh2); 
}

The result is different from that in the book. Mark it.

The syntax structure of the let expression is as follows. The scope of the let variable is restricted to the inside of the expression.

let (var1 [= value1] [, var2 [= value2]] [, ..., varN [= valueN]]) expression;

The following is a specific example of a let expression.

var hzh1 = 1;
var hzh2 = let(hzh1 = 2) hzh1 + 1 ; // The let variable (value 2) is used in the expression hzh1 + 1
console.log(hzh1, hzh2);            // It has no effect on the var variable hzh1

The here is also different from the above.

6.4.4 nested functions and scopes

In JavaScript, we can make nested declarations of functions. In another function, that is to say, in another function. At this time, you can access the scope of its external function in the internal function. Formally, the search of names is from the inside out. At the end, the name in the global scope will be found.

Listing 6.7 is a concrete example. The function declaration statement written in code listing 6.7 has the same effect if an anonymous function expression is used.

Code listing 6.7 nested functions and their scopes
function huangzihan1 () {
    var hzh1 = 1; // Local variable huangzihan1

    // Declaration of nested functions
    function huangzihan2 () {
        var hzh2 = 2; // Local variable of function huangzihan2
        console.log("Pair function huangzihan2 To access local variables:");
        console.log(hzh1); 
        console.log("");
        console.log("Pair function huangzihan2 To access local variables:");
        console.log(hzh2);
    }

    function huangzihan3() {
        console.log(hzh2); // If there is no global variable hzh2, ReferenceError occurs
    }

    // Call of nested function
    huangzihan2();
    huangzihan3();
}

huangzihan1();
[Running] node "e:\HMV\Babel\hzh.js"
Pair function huangzihan2 To access local variables:
1

Pair function huangzihan2 To access local variables:
2
e:\HMV\Babel\hzh.js:15
        console.log(hzh2); // If there is no global variable hzh2, ReferenceError occurs
                    ^

ReferenceError: hzh2 is not defined
    at huangzihan3 (e:\HMV\Babel\hzh.js:15:21)
    at huangzihan1 (e:\HMV\Babel\hzh.js:20:5)
    at Object.<anonymous> (e:\HMV\Babel\hzh.js:23:1)
    at Module._compile (internal/modules/cjs/loader.js:999:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1027:10)
    at Module.load (internal/modules/cjs/loader.js:863:32)
    at Function.Module._load (internal/modules/cjs/loader.js:708:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:60:12)
    at internal/main/run_main_module.js:17:47

[Done] exited with code=1 in 0.371 seconds

6.4.5 variable hiding

The professional term "hiding" is used here. It refers to hiding variables (or functions) with the same name with a larger scope through variables (or functions) with a smaller scope. This often happens unintentionally, resulting in errors. For example, in the following code, the global variable n is hidden by the local variable n.

var hzh1 = 1; // global variable

function huangzihan() { // Local variables hide global variables
    var hzh1 = 2;
    console.log("Check whether global variables are hidden in local variables:");
    console.log(hzh1);
}

// function call
huangzihan();
[Running] node "e:\HMV\Babel\hzh.js"
Check whether global variables are hidden in local variables:
2

[Done] exited with code=0 in 0.24 seconds

The function of this code is obvious. At first glance, the concealment of function scopes like listing 6.3 or listing 6.1 and block level scopes does not cause any problems. However, when the code becomes more complex, the problem is not easy to find, so we still need to pay more attention.

6.5 a function is an object

6.6 Function class

6.7 nested function declaration and closure

6.8 callback function design mode

Posted by argrafic on Fri, 13 May 2022 12:13:06 +0300