Js optional function parameters. Default Arguments


May 24, 2011 at 01:13 Five ways to call a function
  • JavaScript
  • Translation

I often come across JavaScript code where errors are caused by a misunderstanding of how functions work in JavaScript (by the way, much of this code was written by me). JavaScript is a multi-paradigm language, and it has functional programming mechanisms. It's time to explore these possibilities. In this article, I will tell you five ways to call functions in JavaScript.

In the first stages learning JavaScript Beginners usually think that its functions work in much the same way as, say, C#. But the mechanisms for calling functions in JavaScript have a number of important differences, and ignorance of them can result in errors that will not be easy to find.

Let's write a simple function that returns an array of three elements - the current this value and the two arguments passed to the function.
function makeArray(arg1, arg2)( return [ this, arg1, arg2 ]; )

The most common way: global call Beginners often declare functions as shown in the example above. Calling this function is easy:
makeArray("one", "two"); // => [ window, "one", "two" ]
Wait. Where does the window object come from? Why is this equal to window?

In JavaScript, no matter whether the script is executed in the browser or in another environment, it is always defined global object. Any code in our script that is not "bound" to anything (that is, outside of the object declaration) is actually in the context of the global object. In our case, makeArray is not just a function “walking” on its own. In fact, makeArray is a method of the global object (in the case of code execution in the browser) window . It's easy to prove:
alert(typeof window.methodThatDoesntExist); // => undefined alert(typeof window.makeArray); // => function
That is, calling makeArray("one", "two"); is equivalent to calling window.makeArray("one", "two"); .

It saddens me that this is the most common way of calling functions, because it implies the presence of a global function. And we all know that global functions and variables are not the best good tone in programming. This is especially true for JavaScript. Avoid global definitions, and you won't regret it.

Function calling rule #1: If a function is called directly, without specifying an object (for example, myFunction()), the value of this will be the global object (window if the code is executed in the browser).

Calling a Method Let's create a simple object and make makeArray its method. Let's declare the object using literal notation, and then call our method:
// create an object var arrayMaker = ( someProperty: "some value", make: makeArray ); // call the make() method arrayMaker.make("one", "two"); // => [ arrayMaker, "one", "two" ] // alternative syntax, use square brackets arrayMaker["make"]("one", "two"); // => [ arrayMaker, "one", "two" ]
Do you see the difference? The value of this in this case is the object itself. Why not window , as in the previous case, since the function declaration has not changed? The secret is how functions are passed in JavaScript. Function is standard type JavaScript, which is actually an object, and like any other object, functions can be passed and copied. IN in this case, we essentially copied the entire function, including the argument list and body, and assigned the resulting object to the arrayMaker object's make property. This is equivalent to a declaration like this:
var arrayMaker = ( someProperty: "Some value"; make: function (arg1, arg2) ( return [ this, arg1, arg2]; ) );
Function Calling Rule #2: In a function called using method call syntax, such as obj.myFunction() or obj["myFunction"]() , this will have the value obj .

Misunderstanding of this generally simple principle often leads to errors when processing events:
function buttonClicked())( var text = (this === window) ? "window" : this.id; alert(text); ) var button1 = document.getElementById("btn1"); var button2 = document.getElementById("btn2"); button1.onclick = buttonClicked; button2.onclick = function())( buttonClicked(); );
Clicking the first button will show a message "btn1" because in this case we are calling a function as a method, and this inside the function will get the value of the object that this method belongs to. Clicking the second button will give "window" because in this case we are calling buttonClicked directly (i.e. not like obj.buttonClicked()). The same thing happens when we assign an event handler to the element tag, as in the case of the third button. Clicking the third button will show the same message as the second.

When using libraries like jQuery, you don't need to think about this. jQuery will take care to rewrite the this value in the event handler so that the this value is the element that raised the event:
// use jQuery $("#btn1").click(function() ( alert(this.id); // jQuery will make sure "this" is a button ));
How does jQuery manage to change the value of this ? Read below.

Two more ways: apply() and call() It is logical that the more often you use functions, the more often you have to pass them and call them in different contexts. Often there is a need to override the value of this . If you remember, functions in JavaScript are objects. In practice, this means that functions have predefined methods. apply() and call() are two of them. They allow you to override the this value:
var car = ( year: 2008, model: "Dodge Bailout" ); makeArray.apply(car, [ "one", "two" ]); // => [ car, "one", "two" ] makeArray.call(car, "one", "two"); // => [ car, "one", "two" ]
These two methods are very similar. The first parameter overrides this . The differences between them are in the subsequent arguments: Function.apply() accepts an array of values ​​that will be passed to the function, while Function.call() accepts the arguments separately. In practice, in my opinion, it is more convenient to use apply() .

Function Calling Rule #3: If you want to override the value of this without copying the function to another object, you can use myFunction.apply(obj) or myFunction.call(obj) .

Constructors I will not dwell on the ad in detail own types in JavaScript, but I think it is necessary to remind you that there are no classes in JavaScript, and any user-defined type needs a constructor. In addition, methods custom type It's better to declare it using prototype , which is a property of the constructor function. Let's create our own type:
// declare the constructor function ArrayMaker(arg1, arg2) ( this.someProperty = "no matter"; this.theArray = [ this, arg1, arg2 ]; ) // declare methods ArrayMaker.prototype = ( someMethod: function () ( alert( "Called by someMethod"); ), getArray: function () ( return this.theArray; ) ); var am = new ArrayMaker("one", "two"); var other = new ArrayMaker("first", "second"); am.getArray(); // => [ am, "one", "two" ]
The important thing in this example is the presence of the new operator before the function call. If it weren't for it, it would be a global call, and the properties created in the constructor would belong to the global object. We don't need that. Additionally, constructors usually do not return values ​​explicitly. Without the new operator the constructor would return undefined , with it it returns this . Good style the names of constructors are considered to be capitalized; This will remind you of the need for the new operator.

Otherwise, the code inside the constructor will likely be similar to code you would write in another language. The value of this in this case is the new object you are creating.

Function Calling Rule #4: When calling a function with the new operator, the value of this will be a new object created by the JavaScript runtime. If this function does not return any object explicitly, this will be returned implicitly.

Conclusion Hopefully understanding the difference between different ways function calls will allow you to improve your JavaScript code. Sometimes errors related to the this value are difficult to catch, so it makes sense to prevent them in advance.

A function is program code that is defined once and can then be called for execution any number of times.

In JavaScript, a function is a value, so it can be assigned to variables, array elements, object properties, passed as an argument to functions, and returned as a value from functions.

Declaring and calling a function

There are three ways to declare a function: Function Declaration, Function Expression, and Named Function Expression.

Function Declaration (abbreviated as FD) is a “classic” function declaration. In JavaScript, functions are declared using a function literal. FD declaration syntax:

A function literal consists of the following four parts:

  • The keyword is function.
  • A required identifier that specifies the name of the function. A verb is usually chosen as a function name because the function performs an action.
  • A pair of parentheses around a list of zero or more identifiers, separated by commas. These identifiers are called function parameters.
  • The body of a function, consisting of a pair of curly braces containing instructions. The function body can be empty, but braces must always be specified.
  • Simple example:

    Function sayHi() ( alert("Hello"); )

    When encountering the function keyword, the interpreter creates a function and then assigns a reference to it to a variable named sayHi (a variable with this name is created automatically by the interpreter).

    By accessing the sayHi variable, you can see that there is a function there as a value (in fact, a link to it):

    Alert(sayHi); // function sayHi() ( alert("Hello"); )

    A Function Expression (abbreviated as FE) is a function declaration that is part of an expression (such as an assignment). FE declaration syntax:

    Function (parameters) (instructions)

    Simple example:

    Var sayHi = function () ( alert("Hello"); );

    The FE function is also called an "anonymous function".

    A Named Function Expression (NFE for short) is a function declaration that is part of an expression (such as an assignment). NFE declaration syntax:

    Function identifier (parameters) (instructions)

    Simple example:

    Var sayHi = function foo() ( alert("Hello"); );

    FE and NFE declarations are processed by the interpreter in exactly the same way as an FD declaration: the interpreter creates a function and stores a reference to it in the sayHi variable.

    Program code, located in the body of the function, is executed not at the time the function is declared, but at the time it is called. To call a function, use the operator () (function call):

    Function sayHi() ( alert("Hello"); ) var sayHi2 = function () ( alert("Hello2"); ); var sayHi3 = function foo() ( alert("Hello3"); ); sayHi(); // "Hello" sayHi2(); // "Hello2" sayHi3(); // "Hello3"

    The difference between the three declarations presented is that functions declared as FD are created by the interpreter before the code starts executing (at the analysis stage), so they can be called (in the scope where they are declared) before the declaration:

    // Calling a function before it is declared in top-level code foo(); function foo() ( alert("Calling the function foo() in the global scope."); // Calling the function before it is declared in the scope of the function bar(); function bar() ( alert("Calling the function bar() in function scope."); ) )

    Functions declared as FE or NFE are created at runtime, so they can only be called after they are declared:

    // sayHi(); // Error. The function sayHi does not exist yet var sayHi = function () ( alert("Hello!"); ); sayHi();

    Functions declared inside a block are in block scope:

    // foo(); // Error. The function is not declared. ( foo(); // 1 function foo() ( console.log(1); ) ) foo(); // Error. The function is not declared.

    Unlike FE, a function declared as NFE has the ability to refer to itself by name when called recursively. The function name is only available within the function itself:

    (function sayHi(str) ( if (str) ( return; ) sayHi("hi"); // The name is available inside the function ))(); sayHi(); // Error. Function not declared

    Callback function

    A callback function is a function that is passed as an argument to another function to be called later.

    Callback functions are often used as event handlers.

    Below is an example of a function that takes as its argument a reference to another function for subsequent calling:

    Function foo(callback) ( return callback(); ) foo (function() ( alert("Hello!"); ));

    This example clearly demonstrates how a callback works.

    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. On next line a function is declared. As you can see, it all starts with the function keyword. The parameters are indicated inside the brackets, but in this case there are no parameters, so everything inside the brackets is empty. Next come curly braces, inside which is 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 the basic mathematical operations(addition, subtraction, multiplication and division).

    Any programmer is well aware of what functions are and why they are needed. However, functions in Javascript have some special features. If you have been programming in this language for a long time, then you probably know that there are different . If you come from another language, then when reading some articles you most likely saw this, strange at first glance, function declaration:

    Var add = function(arg1, arg2) ( var sum = arg1 + arg2; return sum; ) var result = add(5, 2); //result is now 7

    That is, the function, firstly, does not have a name. Secondly, it is assigned to a variable, but not just assigned, but its body goes right there. Personally, for me, who had previously written in languages ​​such as VB, C++, such an announcement caused bewilderment and misunderstanding of how it works and why write it like that at all.

    I'm used to the "classic" function declaration and call, like this:

    Function add(arg1, arg2) ( var sum = arg1 + arg2; return sum; ) var result = add(5, 3); //result is now 8

    And this is where we come to the features of functions in Javascript. For ease of understanding, imagine that a function in JS is a regular value, like a number or a string. Can you write the number 5 into the result variable? Or something more complex, like an array, and then display it on the screen? You can. So, if you imagine that a function is an ordinary value, albeit of a very complex structure, then the first method of declaration no longer seems incredible.

    The next interesting fact is a logical continuation of the first. After we put data into a variable, we can use the name of that variable to pass the data into another variable:

    Var a = 5; var b = a; alert(b); //will output 5

    The usual thing. Now take a look at this code:

    Var add = function(arg1, arg2) ( var sum = arg1 + arg2; return sum; ) var calcSum = add; alert(calcSum(5, 6)); //will print 11

    Are you starting to guess? Since a function is like a variable, we can “multiply” it through ordinary assignment into other variables, turning them into functions as well. Now calcSum can also add two numbers. However, the code

    Var calcSum = add(1, 1); //calcSum is now equal to 2, this is not a function, but a variable with a number alert(calcSum(5, 6)); //error

    It will not be executed, because in the first line we assigned not the function itself, but the result of its execution (the parentheses indicate that the function needs to be executed and not assigned).

    If you need to call a function on itself, this is done as follows:

    Var calcFact = function fact(val) ( if (val == 1) ? val: val * fact(val - 1); //calculating the factorial using recursion ) alert(calcFact(4)); //will print 24

    Here, when assigning a function to a variable, we named it fact. However, this name will only be available within the function itself and nowhere else. The reasons for this lie in the principle of how the interpreter works and are beyond the scope of the lesson.

    You may be wondering, “Hmm, interesting opportunity! But what is the advantage of this method? Are there situations where this is necessary or is it at least more convenient than a regular ad?” I will not undertake to say that there are situations where it is impossible to do without such an approach, but I can give an example where it reduces the amount of code. Let's say you need to greet a person depending on the time of day:

    Var date = new Date(); var hello = (date.getHours()< 12) ? function() {alert("Доброе утро!")} : (date.getHours() < 18) ? function() {alert("Добрый день!")} : function() {alert("Добрый вечер!")}; hello();

    As you can see, the functions are extremely simple, with a single alert command.

    If we decided to go the “classical route,” we would have to write three separate functions and then call them in the time check condition:

    Function goodMorning() ( alert("Good morning!"); ) function goodAfternoon() ( alert("Good afternoon!"); ) function goodEvning() ( alert("Good evening!"); ) var date = new Date (); (date.getHours()< 12) ? goodMorning() : (date.getHours() < 18) ? goodAfternoon() : goodEvning();

    The code has grown significantly visually, even considering that we used the short form of notation conditional operator. If we assume that the file contains really important functions that perform calculations, then cluttering the list with such mini-functions that do not carry important logic, and which are most likely used only once, is not a good idea. In addition, we are forced to give each function a unique name and indicate it when calling. Therefore, if you need to change the name of one of them, you will have to change it in two places, which increases the likelihood of an error.

    Secondly, if we use the “classical” method, we lose the ability to assign a function to a variable. That is, write

    Function add(a, b) ( return a + b; ) var calcSum = add; calcSum(5, 5);

    It's no longer possible. Therefore, in our example, if we still need to greet the guest more than once, we will have to duplicate this fragment each time:

    (date.getHours()< 12) ? goodMorning() : (date.getHours() < 18) ? goodAfternoon() : goodEvning();

    And in the first case it will be enough to write just hello(); and the result will be the same.

    I told you about an interesting feature of JS functions and gave examples. Thus, you have seen that the ways to call functions in Javascript are not limited to just one type. Even if you cannot immediately find use for these capabilities in your projects, at least you will know that such opportunities exist. And when a really good opportunity comes along, you can reduce the amount of code and avoid unnecessary confusion and errors!

    People think that computer science– this is 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. This is a structuring tool. large programs, reducing repetition, assigning names to 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 the variables were used anywhere in the program, it would be very difficult to ensure that one variable is not used for various 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 platforms To run JavaScript, these cases were handled 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 simple program with multiple 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 tells the computer to very complex issue, which results in 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 next version The power function 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 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 option mental model is to imagine that the function freezes the code in its body and wraps it in packaging. 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. Last line calls the function contained in the variable, and therefore the stored code is 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 while loops add leading zeros to numbers until you get a 3-character string.

    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 like the simplest function, which completes a number with zeros up to three positions, and 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.

    First auxiliary 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.

    Very useful to share different tasks executable by the 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.





    

    2024 gtavrl.ru.