How to access a variable in a javascript function. Global and local variables


Functions are one of the fundamental building blocks in JavaScript. A function is a JavaScript procedure-a set of statements that performs a task or calculates a value. To use a function, you must define it somewhere in the scope from which you wish to call it.

A method is a function that is a property of an object. Read more about objects and methods in Working with objects.

Calling functions

Defining a function does not execute it. Defining the function simply names the function and specifies what to do when the function is called. Calling the function actually performs the specified actions with the indicated parameters. For example, if you define the function square , you could call it as follows:

Square(5);

The preceding statement calls the function with an argument of 5. The function executes its statements and returns the value 25.

Functions must be in scope when they are called, but the function declaration can be hoisted (appear below the call in the code), as in this example:

Console.log(square(5)); /* ... */ function square(n) ( return n * n; )

The scope of a function is the function in which it is declared, or the entire program if it is declared at the top level.

Note: This works only when defining the function using the above syntax (i.e. function funcName()()). The code below will not work. That means, function hoisting only works with function declaration and not with function expression.

Console.log(square); // square is hoisted with an initial value undefined. console.log(square(5)); // TypeError: square is not a function var square = function(n) ( return n * n; )

The arguments of a function are not limited to strings and numbers. You can pass whole objects to a function. The show_props() function (defined in ) is an example of a function that takes an object as an argument.

A function can call itself. For example, here is a function that computes factorials recursively:

Function factorial(n) ( if ((n === 0) || (n === 1)) return 1; else return (n * factorial(n - 1)); )

You could then compute the factorials of one through five as follows:

Var a, b, c, d, e; a = factorial(1); // a gets the value 1 b = factorial(2); // b gets the value 2 c = factorial(3); // c gets the value 6 d = factorial(4); // d gets the value 24 e = factorial(5); //e gets the value 120

There are other ways to call functions. There are often cases where a function needs to be called dynamically, or the number of arguments to a function vary, or in which the context of the function call needs to be set to a specific object determined at runtime. It turns out that functions are, themselves, objects, and these objects in turn have methods (see the Function object). One of these, the apply() method, can be used to achieve this goal.

Function scope

Variables defined inside a function cannot be accessed from anywhere outside the function, because the variable is defined only in the scope of the function. However, a function can access all variables and functions defined inside the scope in which it is defined. In other words, a function defined in the global scope can access all variables defined in the global scope. A function defined inside another function can also access all variables defined in its parent function and any other variable to which the parent function has access.

// The following variables are defined in the global scope var num1 = 20, num2 = 3, name = "Chamahk"; // This function is defined in the global scope function multiply() ( return num1 * num2; ) multiply(); // Returns 60 // A nested function example function getScore() ( var num1 = 2, num2 = 3; function add() ( return name + " scored " + (num1 + num2); ) return add(); ) getScore (); // Returns "Chamahk scored 5"

Scope and the function stack Recursion

A function can refer to and call itself. There are three ways for a function to refer to itself:

  • the function's name
  • an in-scope variable that refers to the function
  • For example, consider the following function definition:

    Var foo = function bar() ( // statements go here );

    Within the function body, the following are all equivalent:

  • bar()
  • arguments.callee()
  • foo()
  • A function that calls itself is called a recursive function. In some ways, recursion is analogous to a loop. Both execute the same code multiple times, and both require a condition (to avoid an infinite loop, or rather, infinite recursion in this case). For example, the following loop:

    Var x = 0; while(x< 10) { // "x < 10" is the loop condition // do stuff x++; }

    can be converted into a recursive function and a call to that function:

    Function loop(x) ( if (x >= 10) // "x >= 10" is the exit condition (equivalent to "!(x< 10)") return; // do stuff loop(x + 1); // the recursive call } loop(0);

    However, some algorithms cannot be simple iterative loops. For example, getting all the nodes of a tree structure (e.g. the DOM) is more easily done using recursion:

    Function walkTree(node) ( if (node ​​== null) // return; // do something with node for (var i = 0; i< node.childNodes.length; i++) { walkTree(node.childNodes[i]); } }

    Compared to the function loop , each recursive call itself makes many recursive calls here.

    It is possible to convert any recursive algorithm to a non-recursive one, but often the logic is much more complex and doing so requires the use of a stack. In fact, recursion itself uses a stack: the function stack.

    The stack-like behavior can be seen in the following example:

    Function foo(i) ( if (i< 0) return; console.log("begin: " + i); foo(i - 1); console.log("end: " + i); } foo(3); // Output: // begin: 3 // begin: 2 // begin: 1 // begin: 0 // end: 0 // end: 1 // end: 2 // end: 3

    Nested functions and closures

    You can nest a function within a function. The nested (inner) function is private to its containing (outer) function. It also forms a closure. A closure is an expression (typically a function) that can have free variables together with an environment that binds those variables (that "closes" the expression).

    Since a nested function is a closure, this means that a nested function can "inherit" the arguments and variables of its containing function. In other words, the inner function contains the scope of the outer function.

    • The inner function can be accessed only from statements in the outer function.
    • The inner function forms a closure: the inner function can use the arguments and variables of the outer function, while the outer function cannot use the arguments and variables of the inner function.

    The following example shows nested functions:

    Function addSquares(a, b) ( function square(x) ( return x * x; ) return square(a) + square(b); ) a = addSquares(2, 3); // returns 13 b = addSquares(3, 4); // returns 25 c = addSquares(4, 5); // returns 41

    Since the inner function forms a closure, you can call the outer function and specify arguments for both the outer and inner function:

    Function outside(x) ( function inside(y) ( return x + y; ) return inside; ) fn_inside = outside(3); // Think of it like: give me a function that adds 3 to whatever you give // ​​it result = fn_inside(5); // returns 8 result1 = outside(3)(5); // returns 8

    Preservation of variables

    Notice how x is preserved when inside is returned. A closure must preserve the arguments and variables in all scopes it references. Since each call provides potentially different arguments, a new closure is created for each call to outside. The memory can be freed only when the returned inside is no longer accessible.

    This is not different from storing references in other objects, but is often less obvious because one does not set the references directly and cannot inspect them.

    Multiply-nested functions

    Functions can be multiply-nested, i.e. a function (A) containing a function (B) containing a function (C). Both functions B and C form closures here, so B can access A and C can access B. In addition, since C can access B which can access A, C can also access A. Thus, the closures can contain multiple scopes; they recursively contain the scope of the functions containing it. This is called scope chaining. (Why it is called "chaining" will be explained later.)

    Consider the following example:

    Function A(x) ( function B(y) ( function C(z) ( console.log(x + y + z); ) C(3); ) B(2); ) A(1); // logs 6 (1 + 2 + 3)

    In this example, C accesses B "s y and A "s x . This can be done because:

  • B forms a closure including A, i.e. B can access A "s arguments and variables.
  • C forms a closure including B.
  • Because B "s closure includes A , C "s closure includes A , C can access both B and A "s arguments and variables. In other words, C chains the scopes of B and A in that order.
  • The reverse, however, is not true. A cannot access C , because A cannot access any argument or variable of B , which C is a variable of. Thus, C remains private to only B .

    Name conflicts

    When two arguments or variables in the scope of a closure have the same name, there is a name conflict. More inner scopes take precedence, so the inner-most scope takes the highest precedence, while the outer-most scope takes the lowest. This is the scope chain. The first on the chain is the inner-most scope, and the last is the outer-most scope. Consider the following:

    Function outside() ( var x = 5; function inside(x) ( return x * 2; ) return inside; ) outside())(10); // returns 20 instead of 10

    The name conflict happens at the statement return x and is between inside "s parameter x and outside "s variable x . The scope chain here is ( inside , outside , global object). Therefore inside "s x takes precedences over outside "s x , and 20 (inside "s x) is returned instead of 10 (outside "s x).

    Closures

    Closures are one of the most powerful features of JavaScript. JavaScript allows for the nesting of functions and grants the inner function full access to all the variables and functions defined inside the outer function (and all other variables and functions that the outer function has access to). However, the outer function does not have access to the variables and functions defined inside the inner function. This provides a sort of encapsulation for the variables of the inner function. Also, since the inner function has access to the scope of the outer function, the variables and functions defined in the outer function will live longer than the duration of the outer function execution, if the inner function manages to survive beyond the life of the outer function. A closure is created when the inner function is somehow made available to any scope outside the outer function.

    Var pet = function(name) ( // The outer function defines a variable called "name" var getName = function() ( return name; // The inner function has access to the "name" variable of the outer //function ) return getName; // Return the inner function, thereby exposing it to outer scopes ) myPet = pet("Vivie"); myPet(); // Returns "Vivie"

    It can be much more complex than the code above. An object containing methods for manipulating the inner variables of the outer function can be returned.

    Var createPet = function(name) ( var sex; return ( setName: function(newName) ( name = newName; ), getName: function() ( return name; ), getSex: function() ( return sex; ), setSex: function(newSex) ( if(typeof newSex === "string" && (newSex.toLowerCase() === "male" || newSex.toLowerCase() === "female")) ( sex = newSex; ) ) ) ) var pet = createPet("Vivie"); pet.getName(); // Vivie pet.setName("Oliver"); pet.setSex("male"); pet.getSex(); // male pet.getName(); // Oliver

    In the code above, the name variable of the outer function is accessible to the inner functions, and there is no other way to access the inner variables except through the inner functions. The inner variables of the inner functions act as safe stores for the outer arguments and variables. They hold "persistent" and "encapsulated" data for the inner functions to work with. The functions do not even have to be assigned to a variable, or have a name.

    Var getCode = (function() ( var apiCode = "0]Eal(eh&2"; // A code we do not want outsiders to be able to modify... return function() ( return apiCode; ); ))() ; getCode(); // Returns the apiCode

    There are, however, a number of pitfalls to watch out for when using closures. If an enclosed function defines a variable with the same name as the name of a variable in the outer scope, there is no way to refer to the variable in the outer scope again.

    Var createPet = function(name) ( // The outer function defines a variable called "name". return ( setName: function(name) ( // The enclosed function also defines a variable called "name". name = name; // How do we access the "name" defined by the outer function? ) ) )

    Using the arguments object

    The arguments of a function are maintained in an array-like object. Within a function, you can address the arguments passed to it as follows:

    Arguments[i]

    where i is the ordinal number of the argument, starting at zero. So, the first argument passed to a function would be arguments . The total number of arguments is indicated by arguments.length .

    Using the arguments object, you can call a function with more arguments than it is formally declared to accept. This is often useful if you don"t know in advance how many arguments will be passed to the function. You can use arguments.length to determine the number of arguments actually passed to the function, and then access each argument using the arguments object.

    For example, consider a function that concatenates several strings. The only formal argument for the function is a string that specifies the characters that separate the items to concatenate. The function is defined as follows:

    Function myConcat(separator) ( var result = ""; // initialize list var i; // iterate through arguments for (i = 1; i< arguments.length; i++) { result += arguments[i] + separator; } return result; }

    You can pass any number of arguments to this function, and it concatenates each argument into a string "list":

    // returns "red, orange, blue, " myConcat(", ", "red", "orange", "blue"); // returns "elephant; giraffe; lion; cheetah; " myConcat("; ", "elephant", "giraffe", "lion", "cheetah"); // returns "sage. basil. oregano. pepper. parsley. " myConcat(". ", "sage", "basil", "oregano", "pepper", "parsley");

    Note: The arguments variable is "array-like", but not an array. It is array-like in that it has a numbered index and a length property. However, it does not possess all of the array-manipulation methods.

    Two factors influenced the introduction of arrow functions: shorter functions and non-binding of this.

    Shorter functions

    In some functional patterns, shorter functions are welcome. Compare:

    Var a = [ "Hydrogen", "Helium", "Lithium", "Beryllium" ]; var a2 = a.map(function(s) ( return s.length; )); console.log(a2); // logs var a3 = a.map(s => s.length); console.log(a3); // logs

    No separate this

    Until arrow functions, every new function defined its own value (a new object in the case of a constructor, undefined in function calls, the base object if the function is called as an "object method", etc.). This has proven to be less than ideal with an object-oriented style of programming.

    Function Person() ( // The Person() constructor defines `this` as itself. this.age = 0; setInterval(function growUp() ( // In nonstrict mode, the growUp() function defines `this` // as the global object, which is different from the `this` // defined by the Person() constructor. this.age++; ), 1000); ) var p = new Person();

    In ECMAScript 3/5, this issue was fixed by assigning the value in this to a variable that could be closed over.

    Function Person() ( var self = this; // Some choose `that` instead of `self`. // Choose one and be consistent. self.age = 0; setInterval(function growUp() ( // The callback refers to the `self` variable of which // the value is the expected object. self.age++; ), 1000); )

    How to create a function in JavaScript? This question is quite common among novice programmers. And this article is written just for such beginners. First you need to understand the question: what is a function? Let's start with this.

    You should remember from school what a function is. If not, then I remind you. A function has certain parameters that it manipulates and returns a result. For example, the function y = 2 * x +5. Here we can set x = 3, and the answer will be y = 11. This is an example of a function from mathematics. There are absolutely similar functions in JavaScript, only here the function can be not just some kind of calculation of some expression, but anything.

    Let's first create a function that displays the message "Hello" 3 times.


    function hello() (
    alert("Hello");
    alert("Hello");
    alert("Hello");
    }
    hello();

    The first line tells you that what follows is a JavaScript script. The next line declares the function. As you can see, it all starts with the function keyword. Parameters are indicated inside the brackets, but in in this case There are no parameters, so everything inside the brackets is empty. Next they go braces, which contains the function code that must be executed when it is called. This code uses the alert() function, which calls an information window in which the text specified by the parameter is written. As a parameter, we pass the string "Hello" to the alert() function (this is an example of a built-in function). We call the alert() function three times.

    When the function is written, you need to put a closing curly brace. On the next line we call the hello() function. I hope you understand what a function is. It is possible that some of you will ask: “What are the advantages? After all, we could simply write alert() three times and the result would be the same.” You are right, and this is what you should do if you need to use this code only once. Imagine that you need this 3, 4 or more times. Of course, it is inconvenient to write this repetitive code all the time. It is much easier to write one word hello(). Do you agree? I think yes. At the end of the article, based on this, I will make one very important conclusion, so read it to the end.

    Now let's talk about functions with parameters. Let's create one of these examples (I will write the function and its call right away, without the tag).

    Function sum (x, y) (
    var sum = x + y;
    document.write(sum);
    }
    sum(5, 4);

    The code is quite transparent, however, let me comment. Again the keyword function , then the name of the function (in this case, let it be sum ). Inside the brackets I indicated two parameters that are required ( x And y). Inside the function I create another variable sum(it’s completely normal to name variables and functions the same), to which I assign the sum x And y(which were transferred). And then I display the result in the browser window. After the function, I call it by passing parameters 5 and 4. You can check and see in the browser window - 9.

    And finally, I’ll say something about return values. In the example above, we immediately printed the result, however, for this function it would be most logical not to print anything, but to return the result. What to do with it next is another question. Let's rewrite the function like this:

    Function sum(x, y) (
    var sum = x + y;
    return sum;
    }
    var z = sum(4, 5) + sum(1,-3);
    document.write(z);

    Pay attention to the return keyword. It returns the result (in our case, the sum of two numbers). So sum(4,5) returns 9 . We add this number to the result of the function sum(1, -3) , that is -2 . As a result, we get 7. And only then we print the result in the browser.

    I hope you appreciated the capabilities of functions, so creating and using functions in JavaScript is a must. And now I will make one important conclusion, a principle that must always be followed: any repeated code must be separated into a separate function. If you display the same 5 lines 20 times (for example), then you need to separate these 5 lines into a separate function. Thus, instead of 100 lines (20 times * 5 lines), write only 20 lines, and even more understandable ones (the name of a function is much simpler than 5 lines of code). I would like to say that if this rule is not followed, then the size of the program can increase tens, and maybe hundreds of times.

    I advise you to practice and, for example, create functions of all basic mathematical operations (addition, subtraction, multiplication and division).

    People think that computer science is an art for geniuses. In reality, it's the other way around - just a lot of people making things that stand on top of each other, as if making a wall of small pebbles.

    Donald Knuth

    You've already seen calls to functions like alert . Functions are the bread and butter of JavaScript programming. The idea of ​​wrapping a piece of program and calling it as a variable is very popular. It is a tool for structuring large programs, reducing repetition, naming subroutines, and isolating subroutines from each other.

    The most obvious use of functions is to create a new dictionary. Inventing words for ordinary human prose is bad form. This is necessary in a programming language.

    The average adult Russian speaker knows approximately 10,000 words. A rare programming language contains 10,000 built-in commands. And the vocabulary of a programming language is more clearly defined, so it is less flexible than a human one. Therefore, we usually have to add our own words to it to avoid unnecessary repetition.

    Function Definition A function definition is a normal variable definition, where the value that the variable receives is a function. For example, the following code defines a variable square, which refers to a function that calculates the square of a given number:

    Var square = function(x) ( return x * x; ); console.log(square(12)); // → 144

    A function is created by an expression starting with the function keyword. Functions have a set of parameters (in this case, only x), and a body containing the instructions that must be executed when the function is called. The body of a function is always enclosed in curly braces, even if it consists of a single statement.

    A function may have several parameters or none at all. In the following example, makeNoise does not have a list of parameters, but power has two:

    Var makeNoise = function() ( console.log("Shit!"); ); makeNoise(); // → Khrya! var power = function(base, exponent) ( var result = 1; for (var count = 0; count< exponent; count++) result *= base; return result; }; console.log(power(2, 10)); // → 1024

    Some functions return a value, like power and square, others don't, like makeNoise, which only produces a side effect. The return statement specifies the value returned by the function. When program processing reaches this instruction, it immediately exits the function and returns this value to the place in the code from which the function was called. return without an expression returns undefined .

    Parameters and scope Function parameters are the same variables, but their initial values ​​are set when the function is called, and not in its code.

    An important property of functions is that variables created within a function (including parameters) are local to that function. This means that in the power example, the result variable will be created every time the function is called, and these individual incarnations of it will have nothing to do with each other.

    This variable locality only applies to parameters and variables created within functions. Variables defined outside of any function are called global because they are visible throughout the program. You can also access such variables inside a function, unless you declare a local variable with the same name.

    The following code illustrates this. It defines and calls two functions that assign a value to the variable x. The first declares it as local, thereby changing only the local variable. The second one does not declare, so working with x inside the function refers to the global variable x defined at the beginning of the example.

    Var x = "outside"; var f1 = function() ( var x = "inside f1"; ); f1(); console.log(x); // → outside var f2 = function() ( x = "inside f2"; ); f2(); console.log(x); // → inside f2

    This behavior helps prevent accidental interactions between functions. If all variables were used anywhere in a program, it would be very difficult to ensure that one variable was not used for different purposes. And if you were to reuse a variable, you would encounter strange effects when third-party code corrupts the values ​​of your variable. By treating function-local variables so that they exist only within the function, the language makes it possible to work with functions as if they were separate little universes, allowing you to not worry about the entire code.

    Nested scoping JavaScript distinguishes more than just between global and local variables. Functions can be defined within functions, resulting in multiple levels of locality.

    For example, the following rather meaningless function contains two more inside:

    Var landscape = function() ( var result = ""; var flat = function(size) ( for (var count = 0; count< size; count++) result += "_"; }; var mountain = function(size) { result += "/"; for (var count = 0; count < size; count++) result += """; result += "\\"; }; flat(3); mountain(4); flat(6); mountain(1); flat(1); return result; }; console.log(landscape()); // → ___/""""\______/"\_

    The flat and mountain functions see the result variable because they are inside the function that defines it. But they can't see each other's count variables because the variables of one function are outside the scope of the other. And the environment outside the landscape function does not see any of the variables defined inside this function.

    In short, within each local scope, you can see all the scopes that contain it. The set of variables available within a function is determined by the location where the function is declared in the program. All variables from the blocks surrounding the function definition are visible - including those defined at the top level in the main program. This approach to scopes is called lexical.

    People who have studied other programming languages ​​may think that any block enclosed in curly braces creates its own local environment. But in JavaScript, only functions create scope. You can use freestanding blocks:

    Var something = 1; ( var something = 2; // Do something with the something variable... ) // Exited the block...

    But something inside the block is the same variable as outside. Although such blocks are allowed, it only makes sense to use them for if statements and loops.

    If this seems strange to you, you are not the only one who thinks so. JavaScript 1.7 introduced the let keyword, which works like var but creates variables that are local to any given block, not just the function.

    Functions as Values ​​Function names are usually used as a name for a piece of program. Such a variable is set once and does not change. So it's easy to confuse a function and its name.

    But these are two different things. A function call can be used like a simple variable - for example, used in any expression. It is possible to store a function call in a new variable, pass it as a parameter to another function, and so on. Also, the variable storing the function call remains a regular variable and its value can be changed:

    Var launchMissiles = function(value) ( ​​missileSystem.launch("or!"); ); if (safeMode) launchMissiles = function(value) (/* cancel */);

    In Chapter 5, we'll discuss the wonderful things you can do by passing function calls to other functions.

    Declaring Functions There is a shorter version of the expression “var square = function...”. The function keyword can be used at the beginning of a statement:

    Function square(x) ( return x * x; )

    This is a function declaration. The statement defines the square variable and assigns the given function to it. So far so good. There is only one pitfall in such a definition.

    Console.log("The future says:", future()); function future() ( return "We STILL have no flying cars."; )

    This code works even though the function is declared below the code that uses it. This is because function declarations are not part of normal top-down program execution. They are "moved" to the top of their scope and can be called by any code in that scope. Sometimes this is convenient because you can write code in the order that makes the most sense without having to worry about having to define all the functions above where they are used.

    What happens if we place a function declaration inside a conditional block or loop? You don't have to do that. Historically, different JavaScript running platforms have handled such cases differently, and the current language standard prohibits this. If you want your programs to run sequentially, use function declarations only within other functions or the main program.

    Function example() ( function a() () // Normal if (something) ( function b() () // Ay-yay-yay! ) )

    Call Stack It's helpful to take a closer look at how execution order works with functions. Here's a simple program with a few function calls:

    Function greet(who) ( console.log("Hello, " + who); ) greet("Semyon"); console.log("Pokeda");

    It is processed something like this: calling greet causes the pass to jump to the beginning of the function. It calls the built-in console.log function, which intercepts control, does its thing, and returns control. Then he gets to the end of greet, and returns to the place where he was called from. The next line calls console.log again.

    This can be shown schematically like this:

    Top greet console.log greet top console.log top

    Because the function must return to the place from which it was called, the computer must remember the context from which the function was called. In one case, console.log should return back to greet. In another, she returns to the end of the program.

    The place where the computer remembers the context is called the stack. Each time a function is called, the current context is pushed to the top of the stack. When the function returns, it pops the top context from the stack and uses it to continue running.

    Stack storage requires memory space. When the stack gets too big, the computer will stop executing and say something like “stack overflow” or “too much recursion.” The following code demonstrates this - it asks the computer a very complex question, which leads to endless jumps between two functions. More precisely, it would be infinite jumps if the computer had an infinite stack. In reality, the stack overflows.

    Function chicken() ( return egg(); ) function egg() ( return chicken(); ) console.log(chicken() + " came first."); // → ??

    Optional Arguments The following code is completely legal and runs without problems:

    Alert("Hello", "Good evening", "Hello everyone!");

    Officially, the function takes one argument. However, when challenged like this, she doesn't complain. She ignores the rest of the arguments and shows "Hello."

    JavaScript is very particular about the number of arguments passed to a function. If you transfer too much, the extra ones will be ignored. Too few and those missing will be assigned the value undefined.

    The downside to this approach is that it is possible - and even likely - to pass the wrong number of arguments to a function without anyone complaining about it.

    The advantage is that you can create functions that take optional arguments. For example, in the next version of the power function, it can be called with either two or one argument - in the latter case, the exponent will be equal to two, and the function works like a square.

    Function power(base, exponent) ( if (exponent == undefined) exponent = 2; var result = 1; for (var count = 0; count< exponent; count++) result *= base; return result; } console.log(power(4)); // → 16 console.log(power(4, 3)); // → 64

    In the next chapter, we'll see how you can find out in the body of a function the exact number of arguments passed to it. This is useful because... allows you to create a function that takes any number of arguments. For example, console.log uses this property and prints all the arguments passed to it:

    Console.log("R", 2, "D", 2); // → R 2 D 2

    Closures The ability to use function calls as variables, coupled with the fact that local variables are created anew each time a function is called, leads us to an interesting question. What happens to local variables when a function stops working?

    The following example illustrates this issue. It declares the wrapValue function, which creates a local variable. It then returns a function that reads this local variable and returns its value.

    Function wrapValue(n) ( var localVariable = n; return function() ( return localVariable; ); ) var wrap1 = wrapValue(1); var wrap2 = wrapValue(2); console.log(wrap1()); // → 1 console.log(wrap2()); // → 2

    This is valid and works as it should - access to the variable remains. Moreover, multiple instances of the same variable can exist at the same time, which further confirms the fact that local variables are recreated with each function call.

    This ability to work with a reference to an instance of a local variable is called a closure. A function that closes local variables is called a closure. Not only does it free you from worrying about variable lifetimes, but it also allows you to use functions creatively.

    WITH small change we turn our example into a function that multiplies numbers by any given number.

    Function multiplier(factor) ( return function(number) ( return number * factor; ); ) var twice = multiplier(2); console.log(twice(5)); // → 10

    A separate variable like localVariable from the wrapValue example is no longer needed. Since the parameter is itself a local variable.

    It will take practice to start thinking this way. A good mental model is to imagine that a function freezes the code in its body and wraps it in a package. When you see return function(...) (...), think of it as a control panel for a piece of code frozen for later use.

    In our example, multiplier returns a frozen piece of code, which we store in the twice variable. The last line calls the function contained in the variable, which causes the stored code to be activated (return number * factor;). It still has access to the factor variable that was defined when calling multiplier, and it also has access to the argument passed during defrost (5) as a numeric parameter.

    Recursion A function may well call itself as long as it takes care not to overflow the stack. This function is called recursive. Here is an example of an alternative implementation of exponentiation:

    Function power(base, exponent) ( if (exponent == 0) return 1; else return base * power(base, exponent - 1); ) console.log(power(2, 3)); // → 8

    This is roughly how mathematicians define exponentiation, and perhaps this describes the concept more elegantly than a cycle. The function calls itself many times with different arguments to achieve multiple multiplication.

    However, this implementation has a problem - in a regular JavaScript environment it is 10 times slower than the version with a loop. Walking through a loop is cheaper than calling a function.

    The speed versus elegance dilemma is quite interesting. There is a certain gap between convenience for humans and convenience for machines. Any program can be sped up by making it larger and more intricate. The programmer is required to find the appropriate balance.

    In the case of the first exponentiation, the inelegant loop is quite simple and straightforward. It doesn't make sense to replace it with recursion. Often, however, programs deal with such complex concepts that one wants to reduce efficiency by increasing readability.

    The basic rule, which has been repeated more than once, and with which I completely agree, is not to worry about performance until you are absolutely sure that the program is slowing down. If so, find the parts that last the longest and trade elegance for efficiency there.

    Of course, we shouldn't completely ignore performance right away. In many cases, as with exponentiation, we do not get much simplicity from elegant solutions. Sometimes an experienced programmer can see right away that the simple approach will never be fast enough.

    I point this out because too many novice programmers are obsessed with efficiency even in small things. The result is larger, more complex and often not without errors. Such programs take longer to write, but they often do not work much faster.

    But recursion is not always just a less efficient alternative to loops. Some problems are easier to solve with recursion. Most often this is a traversal of several branches of a tree, each of which can branch.

    Here's a riddle: you can get an infinite number of numbers by starting with the number 1, and then either adding 5 or multiplying by 3. How do we write a function that, given a number, tries to find the sequence of additions and multiplications that lead to a given number? For example, the number 13 can be obtained by first multiplying 1 by 3 and then adding 5 twice. And the number 15 cannot be obtained this way at all.

    Recursive solution:

    Function findSolution(target) ( function find(start, history) ( if (start == target) return history; else if (start > target) return null; else return find(start + 5, "(" + history + " + 5)") || find(start * 3, "(" + history + " * 3)"); ) return find(1, "1"); ) console.log(findSolution(24)); // → (((1 * 3) + 5) * 3)

    This example does not necessarily find the shortest solution - it is satisfied by any. I don't expect you to immediately understand how the program works. But let's understand this great exercise in recursive thinking.

    The inner function find does recursion. It takes two arguments - the current number and a string that contains a record of how we arrived at this number. And it returns either a string showing our sequence of steps, or null.

    To do this, the function performs one of three actions. If the given number is equal to the goal, then the current story is precisely the way to achieve it, so it returns. If the given number is larger than the goal, there is no point in continuing to multiply and add, because it will only increase. And if we haven't reached the goal yet, the function tries both possible paths starting with the given number. She summons herself twice, once with each method. If the first call does not return null, it is returned. In another case, the second one is returned.

    To better understand how the function achieves its desired effect, let's look at the calls it makes to find a solution to the number 13.

    Find(1, "1") find(6, "(1 + 5)") find(11, "((1 + 5) + 5)") find(16, "(((1 + 5) + 5 ) + 5)") too big find(33, "(((1 + 5) + 5) * 3)") too big find(18, "((1 + 5) * 3)") too big find( 3, "(1 * 3)") find(8, "((1 * 3) + 5)") find(13, "(((1 * 3) + 5) + 5)") found!

    The indentation shows the depth of the call stack. The first time, the find function calls itself twice to check solutions starting with (1 + 5) and (1 * 3). The first call looks for a solution starting with (1 + 5) and uses recursion to check all solutions that produce a number less than or equal to the required number. Doesn't find it and returns null. Then the operator || and moves on to a function call that examines the (1 * 3) option. We're in luck here, because in the third recursive call we get 13. This call returns a string, and each of the || along the way it passes this line higher, resulting in returning a solution.

    Growing Functions There are two more or less natural ways to introduce functions into a program.

    The first is that you write similar code several times. This should be avoided - more code means more room for errors and more reading material for those trying to understand the program. So we take repeating functionality and match it good name and put it in a function.

    The second way is that you discover a need for some new functionality that is worthy of being placed in a separate function. You start with the name of the function, and then write its body. You can even start by writing the code that uses the function before the function itself has been defined.

    How difficult it is for you to name a function shows how well you understand its functionality. Let's take an example. We need to write a program that prints two numbers, the number of cows and chickens on the farm, followed by the words “cows” and “chickens.” You need to add zeros to the numbers in front so that each one occupies exactly three positions.

    007 Cows 011 Chickens

    Obviously, we need a function with two arguments. Let's start coding.
    // print FarmInventory function printFarmInventory(cows, chickens) ( var cowString = String(cows); while (cowString.length< 3) cowString = "0" + cowString; console.log(cowString + " Коров"); var chickenString = String(chickens); while (chickenString.length < 3) chickenString = "0" + chickenString; console.log(chickenString + " Куриц"); } printFarmInventory(7, 11);

    If we add .length to a string, we get its length. It turns out that the while loops add leading zeros to the numbers until they get a line of 3 characters.

    Ready! But just as we were about to send the code to the farmer (along with a hefty check, of course), he calls and tells us that he has pigs on his farm, and could we add a display of the number of pigs to the program?

    Of course it is possible. But when we start copying and pasting the code from these four lines, we realize that we need to stop and think. There must be a better way. We are trying to improve the program:

    // output WITH Adding Zeros AND Labels function printZeroPaddedWithLabel(number, label) ( var numberString = String(number); while (numberString.length< 3) numberString = "0" + numberString; console.log(numberString + " " + label); } // вывестиИнвентаризациюФермы function printFarmInventory(cows, chickens, pigs) { printZeroPaddedWithLabel(cows, "Коров"); printZeroPaddedWithLabel(chickens, "Куриц"); printZeroPaddedWithLabel(pigs, "Свиней"); } printFarmInventory(7, 11, 3);

    Works! But the name printZeroPaddedWithLabel is a bit strange. It combines three things—output, adding zeros, and a label—into one function. Instead of inserting an entire repeating fragment into a function, let's highlight one concept:

    // add Zero function zeroPad(number, width) ( var string = String(number); while (string.length< width) string = "0" + string; return string; } // вывестиИнвентаризациюФермы function printFarmInventory(cows, chickens, pigs) { console.log(zeroPad(cows, 3) + " Коров"); console.log(zeroPad(chickens, 3) + " Куриц"); console.log(zeroPad(pigs, 3) + " Свиней"); } printFarmInventory(7, 16, 3);

    A function with a nice, clear name zeroPad makes the code easier to understand. And it can be used in many situations, not only in our case. For example, to display formatted tables with numbers.

    How smart and versatile should the features be? We can write either a simple function that pads a number with zeros up to three positions, or a sophisticated function general purpose for formatting numbers, supporting fractions, negative numbers, dot alignment, padding with different characters, etc.

    A good rule of thumb is to add only functionality that you know will be useful. Sometimes it's tempting to create general-purpose frameworks for every little need. Resist him. You'll never finish the job, you'll just end up writing a bunch of code that no one will use.

    Functions and Side Effects Functions can be roughly divided into those that are called for their side effects and those that are called to obtain some value. Of course, it is also possible to combine these properties in one function.

    The first helper function in the farm example, printZeroPaddedWithLabel, is called because it has a side effect: it prints a string. The second, zeroPad, because of the return value. And it’s not a coincidence that the second function comes in handy more often than the first. Functions that return values ​​are easier to combine with each other than functions that produce side effects.

    A pure function is a special kind of value-returning function that not only has no side effects, but also does not depend on the side effects of the rest of the code - for example, it does not work with global variables that could be accidentally changed somewhere else. A pure function, when called with the same arguments, returns the same result (and does nothing else) - which is quite nice. She's easy to work with. A call to such a function can be mentally replaced by the result of its work, without changing the meaning of the code. When you want to test such a function, you can simply call it, and be sure that if it works in a given context, it will work in any context. Less pure functions may return different results depending on many factors, and have side effects that are difficult to test and account for.

    However, you should not be embarrassed to write functions that are not entirely pure, or to begin a sacred code cleansing of such functions. Side effects are often beneficial. There is no way to write a clean version of the console.log function, and this function is quite useful. Some operations are easier to express using side effects.

    Summary This chapter showed you how to write your own functions. When the function keyword is used as an expression, returns a pointer to the function call. When used as an instruction, you can declare a variable by assigning a function call to it.

    The key to understanding functions is local scope. Parameters and variables declared inside a function are local to it, are recreated each time it is called, and are not visible from the outside. Functions declared inside another function have access to its scope.

    It is very useful to separate the different tasks performed by a program into functions. You don't have to repeat yourself; functions make code more readable by dividing it into meaningful parts, just as chapters and sections of a book help organize regular text.

    ExercisesMinimum In the previous chapter, we mentioned the Math.min function, which returns the smallest of its arguments. Now we can write such a function ourselves. Write a min function that takes two arguments and returns the minimum of them.

    Console.log(min(0, 10)); // → 0 console.log(min(0, -10)); // → -10

    Recursion We have seen that the % (modulo) operator can be used to determine whether a number (%2) is even. Here's another way to define it:

    Zero is even.
    The unit is odd.
    Any number N has the same parity as N-2.

    Write a recursive function isEven according to these rules. It must accept a number and return a boolean value.

    Test it at 50 and 75. Try giving it -1. Why is she acting this way? Is it possible to somehow fix it?

    Test it on 50 and 75. See how it behaves on -1. Why? Can you think of a way to fix this?

    Console.log(isEven(50)); // → true console.log(isEven(75)); // → false console.log(isEven(-1)); // → ??

    Counting the beans.

    The character number N of a string can be obtained by adding .charAt(N) (“string”.charAt(5)) to it - in a similar way to getting the length of a string using .length. The return value will be a string consisting of one character (for example, “k”). The first character of the string has position 0, which means that the last character will have position string.length - 1. In other words, a string of two characters has length 2, and its character positions will be 0 and 1.

    Write a function countBs that takes a string as an argument and returns the number of “B” characters contained in the string.

    Then write a function called countChar, which works something like countBs, but takes a second parameter - the character we'll be looking for in the string (instead of just counting the number of "B" characters). To do this, rework the countBs function.

    The ideas of dynamically generating content on a web resource have become the norm. Static pages and template website building have finally completed their mission.

    However, a modern web resource does not necessarily have to be represented by a set of pages generated by the server and updated by the browser (JS+AJAX).

    When a visitor arrives, a web resource can consist of a couple of headers for the protocol, some text in the “head”, a few lines of code in the “body” and that’s it. The rest " will come up with an idea” during the visitor's experience - this is an ideal site or aspiring to be such.

    Place of description and essence of functions

    JavaScript is an experience gained over many decades. It has a significant development history and a modern, qualified team of creators and developers. The language is well thought out, reliable, beautiful and gives developers a real opportunity to write decent code and improve themselves.

    The concept of an algorithm outside a function is absent here in principle. Of course, the developer can insert a script anywhere on the page, place the code in it, and it will be executed. But what is the point of code that is executed only once: when the page is loaded (reloaded)? Unless it is possible to set the initial values ​​of some unimportant variables.

    A script is a place for describing the necessary variables and functions, rather than a good piece of code written for its own sake. It is the set of functions that is essential and significant, perhaps their mutual direct connection, but more often everything is different. The place where a function is described and the place where it is applied are not at all the same thing.

    It is not at all necessary that a function will call another function directly; it can do this indirectly through dynamic code generation. The visitor makes a decision within the framework of this code and a completely different system of functions is triggered.

    Functional dynamics

    Functional dynamics are not only and not so much the handlers assigned to page elements, they are the functions that form the page elements, and the direct handlers can also change.

    The action on the page unfolds depending on its elements and the behavior of the visitor on it. Mouse movements, keyboard buttons, clicks, element events and other circumstances lead to the launch of the necessary functions.

    Initially there is no sequence and there is no parallelism. There is an adequate response of the web resource to events. How quickly JavaScript will perform a particular function depends on many technical (computer, communication lines) and semantic (algorithm logic, subject area, meaning of the task) factors.

    In fact, one can say that something worked in parallel, and something will be fulfilled after something, but there is no particular meaning in this. It is important that JavaScript functions provide the ability to create an adequate response to visitor actions.

    This is new thinking in development: distributed processing information in the depths of a single browser!

    Syntax of Variables and Functions

    JavaScript variables are placed both in the “script” tag and in the body of the function. Functions are defined in the same way. There is no particular point in writing another function inside a function, but this may be necessary for various and well-founded reasons.

    A function description generally begins with the keyword "function", followed by its name, a list of arguments in parentheses separated by commas, and the function body in curly braces.

    IN in this example two functions are described that provide AJAX exchange between the page and the server. The scXHR variable is described above, therefore it is available both in InitXML and inside WaitReplySC.

    Function name and "function" parameter

    An asynchronous option was introduced here, where the JavaScript function in the function is called after the server responds. At the same time, having received a response from the server, WaitReplySC accesses the page tags, fills them with the received information and calls other functions that may well initiate the next request to the server.

    It's also important to note here that WaitReplySC is a function. But in the line scXHR.onreadystatechange = WaitReplySC it is passed as a parameter. This is a general rule for passing functions to other functions as parameters. Specify brackets and pass its parameter(s) into them - the function will be executed immediately. He only gave me his name, but so what. The function call will be made by the one who received its name.

    Functionality implemented through AJAX allows you to make a call to a JavaScript function through data received from the server. In fact, when sending a request to the server, a particular function may not “know” at all which function it is accessing and with what information.

    Function exit and its result

    In the body of the function, you can write any operators of the language, which, in fact, is intended for this purpose. Variables defined inside and outside of a function are available within a function, but not those defined in other functions.

    If you want a function to return a result, you can use the JavaScript return statement: return. There can be a sufficient number of return statements in the function body. It is not at all necessary that they all return the same type of result.

    Typically, developers highly respect this feature and, depending on the situation, decide to exit the feature as soon as possible.

    It is not at all necessary to go through the entire function algorithm when you can exit earlier.

    Function Arguments

    Arguments to a function are passed as a comma-separated list, enclosed in parentheses and located immediately after its name. Variable names are used as arguments, but values ​​can also be passed directly. To pass a function to a function in JavaScript, you just need to specify its name without parentheses.

    Inside the function, the arguments variable is available, which has the length property. You can access any function argument through arguments , arguments , ... until the last arguments .

    Changing a function argument is valid inside the function, but not outside it. In order to change something outside the function, you need to use the JavaScript return operator, through which you pass the required value outside.

    After the function completes, everything associated with its execution will be destroyed. During execution, a function can change external variables, except those described in other functions, including internal ones.

    arguments has a callee property, which is used to call a function, which is done in this moment time. If you call itself, the JavaScript version of a function within a function allows you to implement recursion.

    Using Functions

    The main concern of functions is to serve browser events. To do this, in almost every tag it is possible to specify the name of the event and the function that processes it. Multiple events can be specified, but only one function is specified per event.

    One function can serve multiple page elements and multiple events. Using the “this” parameter, you can pass information to the function where it was called from.

    A classic use of JS functions is event handlers on elements. In this example, the scfWecomeGo() or scfWelcomeCancel() function will be called in the visitor login/logout form, and scfMenuItemClick(this) when selecting the operating mode.

    In the latter case, the “this” parameter is passed, which allows you to miraculously find out which div the call came from. In general, JavaScript is so well implanted into the DOM and it allows you to navigate through its elements so conveniently and collect the necessary information that the dynamics of the page can be simply unpredictable.

    A function does not have to return a string of characters, a number, or another function. It can return a full-fledged HTML element, which will contain the required number of elements, with its own event handlers.

    By placing such an element on the page, the developer creates new functionality, which is good in terms of solving the problem and satisfying the interests of visitors, but quite difficult in terms of implementation.

    When starting such a full-featured development, it is easy to get confused in your own code, in function calls, in the moments when this or that content of this or that part of the page is formed. Before taking this direction of development, it doesn't hurt to weigh everything carefully.

    About distributed thinking

    The developer has to think at the level of all page elements, at the level of all events, and have a clear idea of ​​how everything actually happens. It's difficult, but the work is worth it.

    In JavaScript, the execution of a function can be deferred until some event occurs, and there can be many such functions, and events tend to propagate and fall into the “sphere of visibility” of various handlers.

    In this example, a function was called somewhere earlier that initiated the creation of a file navigation menu item. A page organization is assumed, that is, there are only seven files in the window that can be deleted and processed. You can navigate either by clicking on a line of the file, using arrows on the keyboard, or in blocks of seven lines.

    Each case has its own functions. In other words, in such a simple example it is necessary to write a couple of dozen functions that will respond to various events, and some of these functions will process various options and situations that are not related to events at all.

    For example, when you delete a row, the lower ones should move up. To do this, you will either need to make a new selection, which is trivial and resource-intensive, or recalculate the lines, use array functions in javascript and elegantly achieve the goal.

    Function Arguments and Results

    JavaScript allows you to bring your code to a "fully functional" state. It's normal for a function's argument to be a function. An option is allowed when the function returns a function. JavaScript is completely relaxed about this.

    This is a good mechanism, but quite complex in terms of implementation. Technically, everything is permissible; only a qualified developer can semantically provide the logic for transferring the “functionality”.

    When in JavaScript there is a function within a function, everything goes well, but when a function generates a function, and that one generates another, then it is quite difficult to follow the logic. Essentially, it's not a question of applying the qualification, it's a question of getting a safe and correct result.

    The developer's concern is clear and simple. There is a problem, you need a solution, not an error like “JavaScript error the operation is insecure,” a blank screen, or stopping the entire browser engine.

    If the argument is a function, then the developer is passing a variable with special properties, that is, it is not a number, not a string, not an object. But using such an argument can lead to external variables changing and the result of the function being executed. Depending on what is transmitted, adequate changes will be made.

    Execution of generated code

    You can implement the execution of code generated during the operation of other code using “eval”. This is not considered a great solution, but often you can not complicate the code with unnecessary functions, but limit yourself to the banal formation of a line of JavaScript code and simply execute it.

    In this example, a line is generated to insert some information into the current div. The div number and information content are different for different positions, so such a solution in this situation is not guaranteed to provide the “javascript error the operation is insecure” situation, but it will reliably give the desired effect.

    A nuance of the JavaScript “function within a function” paradigm

    If there is an opportunity to do without frills, it is better to take advantage of it. All of the options listed are good. Of course, in many cases this is the only solution.

    A classic example of recursion: calculating factorial. It’s quite difficult to write an algorithm that goes into a loop, but it’s very easy to go beyond the boundaries of the value. The factorial is growing too quickly.

    However, both recursion and a function calling another function that can make a valid callback are the norm.

    For example, a regular table. There may be other tables in the table. Nesting cannot be limited. Writing your own set of functions for each table is too much of a luxury.

    There are many such examples that can be given, and all of these will be real and pressing tasks, not at all from the field of programming. That is why the problem lies precisely in the fact that it is impossible to do without frills; the created system of functions, or rather its debugging and subsequent reliable operation, becomes the concern not of JavaScript, but of the developer.

    In this chapter:

    Functions are one of the main ways to combine statements into logically related blocks. In JavaScript, a function is a group of expressions that perform a specific task, combined under a common name.

    Unlike most other programming languages, JavaScript does not differentiate between actual functions and procedures. Like functions, procedures are also program blocks. However, the results of executing procedures directly affect the execution of the program, while functions must return values. From this point of view, JavaScript functions can also be considered as procedures.

    Defining and calling functions

    Before calling and using a function, it must be defined. Defining functions in JavaScript has the following syntax:

    Function FunctionName (arguments) ( expression block )

    Thus, the function consists of the following parts, preceded by keyword function:

    • an identifier defining the name of the function;
    • a list of arguments enclosed in parentheses and separated by commas;
    • JavaScript statements enclosed in curly braces. These statements can involve calls to other functions or even to the function itself (recursion).

    In the simplest case, there may be no arguments, and the block of operations can be represented by a single operator:

    Function MyFirstFunc () ( var MyMessage="This is my function!"; alert(MyMessage); )

    Here we have defined a function that will display a window with the message “This is my function!” It should be noted that even if a function does not take any arguments, it should still have a pair of parentheses after its name.

    ATTENTION
    An important note should be made regarding variables declared in function bodies. Such variables are visible to the program only within the function in which they are defined. So, in the MyFirstFunc example, access to the MyMessage variable is only possible inside this function, but not outside it.

    But more often than not, functions do take some values ​​as their arguments. Let's take as an example the previously discussed block that calculates the list of numbers by which 100 is divisible without a remainder. If you move this block into a separate function, you can use it to display a list of divisors for any number. To do this, we need only one argument, which will determine the number for which we need to get such a list:

    Function remainder_free(j) ( var i=0; while (i





    

    2024 gtavrl.ru.