Создание функций в JavaScript. Функции JavaScript

Создание функций в JavaScript. Функции JavaScript

Любому программисту хорошо известно, что такое функции и зачем они нужны. Однако функциям в языке Javascript присущи некоторые особенности. Если вы давно программируете именно на этом языке, то наверняка знаете, что существуют разные . Если же вы пришли из другого языка, то при прочтении некоторых статей скорее всего видели вот такое, странное на первый взгляд, объявление функции:

Var add = function(arg1, arg2) { var sum = arg1 + arg2; return sum; } var result = add(5, 2); //result теперь 7

То есть функция, во-первых, не имеет имени. Во-вторых, она присваивается переменной, но не просто присваивается, а тут же идет ее тело. Лично у меня, до этого писавшего на таких языках как VB, C++, подобное объявление вызывало недоумение и непонимание, как это устроено и зачем вообще так писать.

Я привык к "классическому" объявлению функции и ее вызову, вот так:

Function add(arg1, arg2) { var sum = arg1 + arg2; return sum; } var result = add(5, 3); //result теперь 8

И вот здесь как раз мы подходим к особенностям функций в Javascript. Для простоты понимания представьте себе, что функция в JS - это обычное значение, как, например, число или строка. Вы же можете записать в переменную result число 5? Или что-то более сложное, вроде массива, а потом вывести это на экран? Можете. Так вот, если представить, что функция - это обычное значение, пусть и весьма сложной структуры, то первый способ объявления уже не кажется чем-то невероятным.

Следующий интересный факт является логическим продолжением первого. После того как мы помещаем данные в переменную, мы можем посредством имени этой переменной передать данные в другую переменную:

Var a = 5; var b = a; alert(b); //выведет 5

Обычное дело. А теперь взгляните вот на этот код:

Var add = function(arg1, arg2) { var sum = arg1 + arg2; return sum; } var calcSum = add; alert(calcSum(5, 6)); //выведет 11

Начинаете уже догадываться? Поскольку функция - как переменная, мы можем "размножать" ее посредством обычного присваивания в другие переменные, превращая их так же в функции. Теперь calcSum тоже умеет складывать два числа. Однако код

Var calcSum = add(1, 1); //calcSum теперь равно 2, это не функция, а переменная с числом alert(calcSum(5, 6)); //ошибка

Не выполнится, поскольку в первой строке мы присвоили не саму функцию, а результат ее выполнения (круглые скобки говорят о том, что нужно выполнить функцию, а не присвоить).

Если вам понадобится вызвать функцию в самой себе, то это делается следующим образом:

Var calcFact = function fact(val) { if (val == 1) ? val: val * fact(val — 1); //вычисление факториала с помощью рекурсии } alert(calcFact(4)); //выведет 24

Здесь, присваивая функцию переменной, мы задали ей имя fact. Однако это имя будет доступно только внутри самой функции и нигде больше. Причины этого кроются в принципе работы интерпретатора и выходят за рамки урока.

Возможно, вы задаетесь вопросом "Хм, интересная возможность! Но какое преимущество у этого способа? Есть ли ситуации, когда без этого не обойтись или это хотя бы более удобно, чем обычное объявление?". Не возьмусь утверждать, что есть ситуации, где без такого подхода обойтись нельзя, но пример, где он уменьшает количество кода, привести могу. Допустим, вам нужно поприветствовать человека в зависимости от времени суток:

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

Как видите, функции крайне простые, с одной-единственной командой alert.

Если бы мы решили пойти "классическим путем", то пришлось бы писать три отдельные функции, а потом вызывать их в условии проверки времени:

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

Код значительно увеличился визуально, даже учитывая, что мы использовали краткую форму записи условного оператора. Если допустить, что файл содержит действительно важные функции, выполняющие вычисления, то засорять список вот такими мини-функциями, не несущими важной логики, да и которые используются, скорее всего, лишь однажды, не лучшая идея. К тому же мы вынуждены каждой функции давать уникальное имя и указывать его при вызове. Поэтому, если понадобится изменить название одной из них, то придется менять его в двух местах, что повышает вероятность возникновения ошибки.

Во-вторых, если мы используем "классический" метод, то лишаемся возможности присвоить функцию переменной. То есть написать

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

Уже нельзя. Поэтому в нашем примере, если все-таки понадобится приветствовать гостя не единожды, нам придется каждый раз дублировать вот этот фрагмент:

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

А в первом случае достаточно будет написать всего лишь hello(); и результат будет тот же.

Я рассказал вам про интересную особенность функций JS и привел примеры. Таким образом вы увидели, что способы вызова функций в Javascript не ограничиваются одним-единственным видом. Даже если вы и не сможете сразу найти применение этим возможностям в ваших проектах, то хотя бы будете знать, что такие возможности есть. И когда подвернется действительно удачный случай, то сможете сократить объем кода и избежать лишней путаницы и ошибок.!

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 scopes 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 proved 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); }

    Встроенная функция описывает какие-либо действия, которые она может совершить при её вызове. Описание действий встроенных функций скрыто от программиста. Примером таких действий может служить вычисление математического выражения и возвращение результата или манипуляция с содержимым html-документа. Функция может принимать параметры или аргументы, с которыми она производит какие-то действия. Аргументами могут служить как , так и .

    Вызов функции производится следующим образом:

    var a = functionName(par1, par2, …, parN);

    В js существует множество встроенных функций для подсчёта математических выражений. Например, функция Math.sin, возвращает синус угла (угол задаётся в радианах), функция Math.sqrt вычисляет корень квадратный из числа, переданного ей в качестве параметра и т.д.

    Например, вычислим корень квадратный из 256.

    var b = Math.sqrt(256);

    После выполнения кода одного из двух вышепредставленных примеров в переменной b будет содержаться значение 16.

    Сегодня мы также рассмотрим две функции, которые позволят вашим скриптам хоть как-то сообщить какую-нибудь информацию внешнему миру. Вы вряд ли будете их использовать при написании реальных проектов, но они помогут вам в обучении.

    Функция alert

    Эта функция принимает в качестве параметра текстовое представление значение, которое она выведет на экран в унылом сером окошке с кнопкой «Ок». Пока пользователь не нажмёт эту кнопку, выполнения скрипта не продолжится.

    Функция document.write

    Эта функция пишет в html-документ текстовое представление значения переданного ей в качестве параметра. Никогда не используйте этот метод при написании скриптов, для реальных проектов, если, конечно, точно не знаете, что делаете. Если вызывать эту функцию после загрузки страницы, то, скорее всего, вы увидите пустую страницу с текстом, который вывел последний вызов этой функции.

    Объект Math

    В этом объекте содержатся функции для вычисления математических выражений и некоторые константы. Об объектах мы поговорим попозже. Для того, чтобы использовать объект Math вам надо лишь запомнить, что обращение к его свойствам (функциям и константам) необходимо написать Math.имяФункцииИлиКонстанты.

    Свойства, которые содержит объект Math (слово «Math» опущено):

    1. Константы

    E 2.718281828459045
    LN10 2.302585092994046 (логарифм натуральный 10)
    LN2 0.6931471805599453 (логарифм натуральный 2)
    LOG10E 0.4342944819032518 (логарифм десятичный e)
    LOG2E 1.4426950408889634 (логарифм по основанию 2 числа e)
    PI 3.141592653589793
    SQRT1_2 0.7071067811865476 (корень квадратный из 0.5)
    SQRT2 1.4142135623730951 (корень квадратный из двух)

    2. Тригонометрические функции

    sin – синус
    cos – косинус
    tan – тангенс

    Угол, который в качестве аргумента принимают эти функции задаётся в радианах, а не в градусах. Для того, чтобы перевести значение угла из градусов в радианы необходимо умножить его на Math.PI и разделить на 180. И наоборот для того, чтобы перевести значение угла из радианов в градусы необходимо умножить его на 180 и разделить на Math.PI.

    Таким образом, 60 градусов это π / 3 радиан, 30 градусов это π / 6 радиан и 45 градусов π / 4 радиан.

    3. Обратные тригонометрические функции

    acos – арккосинус от числа т.е. такой угол (в радианах), косинус которого равен аргументу
    asin – арксинус от числа т.е. такой угол (в радианах), синус которого равен аргументу
    atan – арктангенс от числа т.е. такой угол (в радианах), тангенс которого равен аргументу
    atan2 – арктангенс от частного двух аргументов

    3. Другие функции

    abs – модуль числа
    floor – целая часть числа, “пол“ или округление в меньшую сторону. Обратите внимание, что, например Math.floor(-0.9) и Math.floor(-0.1) это -1, а не ноль.
    ceil – округление в большую сторону или «потолок»
    exp – возвращает значение выражения e x , где x – это аргумент функции
    log – возвращает натуральный логарифм числа
    pow – принимает два аргумента и возвращает степень основание которой это первый аргумент, а показатель – второй.
    max – принимает произвольное количество параметров и возвращает максимальный из них
    min – принимает произвольное количество параметров и возвращает минимальный из них
    random – возвращает случайное значение от 0 до 1
    round – округляет число до единиц
    sqrt – вычисляет корень квадратный из числа

    Многие другие встроенные функции мы рассмотрим по ходу следующих уроков, а на сегодня всё.

    Последнее обновление: 09.04.2018

    Функции представляют собой набор инструкций, выполняющих определенное действие или вычисляющих определенное значение.

    Синтаксис определения функции:

    Function имя_функции([параметр [, ...]]){ // Инструкции }

    Определение функции начинается с ключевого слова function , после которого следует имя функции. Наименование функции подчиняется тем же правилам, что и наименование переменной: оно может содержать только цифры, буквы, символы подчеркивания и доллара ($) и должно начинаться с буквы, символа подчеркивания или доллара.

    После имени функции в скобках идет перечисление параметров. Даже если параметров у функции нет, то просто идут пустые скобки. Затем в фигурных скобках идет тело функции, содержащее набор инструкций.

    Определим простейшую функцию:

    Function display(){ document.write("функция в JavaScript"); }

    Данная функция называется display() . Она не принимает никаких параметров и все, что она делает, это пишет на веб-страницу строку.

    Однако простого определения функции еще недостаточно, чтобы она заработала. На надо еще ее вызвать:

    function display(){ document.write("функция в JavaScript"); } display();

    Необязательно давать функциям определенное имя. Можно использовать анонимные функции:

    Var display = function(){ // определение функции document.write("функция в JavaScript"); } display();

    Фактически мы определяем переменную display и присваиваем ей ссылку на функцию. А затем по имени переменной функция вызывается.

    Также мы можем динамически присваивать функции для переменной:

    Function goodMorning(){ document.write("Доброе утро"); } function goodEvening(){ document.write("Добрый вечер"); } var message = goodMorning; message(); // Доброе утро message = goodEvening; message(); // Добрый вечер

    Параметры функции

    Рассмотрим передачу параметров:

    Function display(x){ // определение функции var z = x * x; document.write(x + " в квадрате равно " + z); } display(5); // вызов функции

    Функция display принимает один параметр - x. Поэтому при вызове функции мы можем передать для него значение, например, число 5, как в данном случае.

    Если функция принимает несколько параметров, то с помощью spread-оператора... мы можем передать набор значений для этих параметров из массива:

    Function sum(a, b, c){ let d = a + b + c; console.log(d); } sum(1, 2, 3); let nums = ; sum(...nums);

    Во втором случае в функцию передается числа из массива nums. Но чтобы передавался не просто массив, как одно значение, а именно числа из этого массива, применяется spread-оператор (многоточие...).

    Необязательные параметры

    Функция может принимать множество параметров, но при этом часть или все параметры могут быть необязательными. Если для параметров не передается значение, то по умолчанию они имеют значение "undefined".

    Function display(x, y){ if(y === undefined) y = 5; if(x === undefined) x = 8; let z = x * y; console.log(z); } display(); // 40 display(6); // 30 display(6, 4) // 24

    Здесь функция display принимает два параметра. При вызове функции мы можем проверить их значения. При этом, вызывая функцию, необязательно передавать для этих параметров значения. Для проверки наличия значения параметров используется сравнение со значением undefined .

    Есть и другой способ определения значения для параметров по умолчанию:

    Function display(x = 5, y = 10){ let z = x * y; console.log(z); } display(); // 50 display(6); // 60 display(6, 4) // 24

    Если параметрам x и y не передаются значения, то они получаются в качестве значений числа 5 и 10 соответствено. Такой способ более лаконичен и интуитивен, чем сравнение с undefined.

    При этом значение параметра по умолчанию может быть производным, представлять выражение:

    Function display(x = 5, y = 10 + x){ let z = x * y; console.log(z); } display(); // 75 display(6); // 96 display(6, 4) // 24

    В данном случае значение параметра y зависит от значения x.

    При необходимости мы можем получить все переданные параметры через глобально доступный массив arguments :

    Function display(){ var z = 1; for(var i=0; i [ window, "one", "two" ]
    Погодите. Откуда взялся объект window ? Почему это у нас this равен window ?

    В JavaScript, неважно, выполняется ли скрипт в браузере или в ином окружении, всегда определен глобальный объект . Любой код в нашем скрипте, не «привязанный» к чему-либо (т.е. находящийся вне объявления объекта) на самом деле находится в контексте глобального объекта. В нашем случае, makeArray - не просто функция, «гуляющая» сама по себе. На самом деле, makeArray - метод глобального объекта (в случае исполнения кода в браузере) window . Доказать это легко:
    alert(typeof window.methodThatDoesntExist); // => undefined alert(typeof window.makeArray); // => function
    То есть вызов makeArray("one", "two"); равносилен вызову window.makeArray("one", "two"); .

    Меня печалит тот факт, что этот способ вызова функций наиболее распространен, ведь он подразумевает наличие глобальной функции. А мы все знаем, что глобальные функции и переменные - не самый хороший тон в программировании. Особенно это справедливо для JavaScript. Избегайте глобальных определений, и не пожалеете.

    Правило вызова функций №1: Если функция вызывается напрямую, без указания объекта (например, myFunction()), значением this будет глобальный объект (window в случае исполнения кода в браузере).

    Вызов метода Давайте создадим простой объект и сделаем makeArray его методом. Объект объявим с помощью литеральной нотации, а после вызовем наш метод:
    // создаем объект var arrayMaker = { someProperty: "какое-то значение", make: makeArray }; // вызываем метод make() arrayMaker.make("one", "two"); // => [ arrayMaker, "one", "two" ] // альтернативный синтаксис, используем квадратные скобки arrayMaker["make"]("one", "two"); // => [ arrayMaker, "one", "two" ]
    Видите разницу? Значение this в этом случае - сам объект. Почему не window , как в предыдущем случае, ведь объявление функции не изменилось? Весь секрет в том, как передаются функции в JavaScript. Function - это стандартный тип JavaScript, являющийся на самом деле объектом, и как и любой другой объект, функции можно передавать и копировать. В данном случае, мы как бы скопировали всю функцию, включая список аргументов и тело, и присвоили получившийся объект свойству make объекта arrayMaker . Это равносильно такому объявлению:
    var arrayMaker = { someProperty: "Какое-то значение"; make: function (arg1, arg2) { return [ this, arg1, arg2]; } };
    Правило вызова функций №2: В функции, вызванной с использованием синтаксиса вызова метода, например, obj.myFunction() или obj["myFunction"]() , this будет иметь значение obj .

    Непонимание этого простого, в общем-то, принципа часто приводит к ошибкам при обработке событий:
    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(); };
    Щелчок по первой кнопке покажет сообщение «btn1» , потому что в данном случае мы вызываем функцию как метод, и this внутри функции получит значение объекта, которому этот метод принадлежит. Щелчок по второй кнопке выдаст «window» , потому что в этом случае мы вызываем buttonClicked напрямую (т.е. не как obj.buttonClicked()). То же самое происходит, когда мы назначаем обработчик события в тэге элемента, как в случае третьей кнопки. Щелчок по третьей кнопке покажет то же самое сообщение, что и для второй.

    При использовании библиотек вроде jQuery думать об этом не надо. jQuery позаботится о том, чтобы переписать значение this в обработчике события так, чтобы значением this был элемент, вызвавший событие:
    // используем jQuery $("#btn1").click(function() { alert(this.id); // jQuery позаботится о том, чтобы "this" являлась кнопкой });
    Каким образом jQuery удается изменить значение this ? Читайте ниже.

    Еще два способа: apply() и call() Логично, что чем чаще вы используете функции, тем чаще вам приходится передавать их и вызывать в разных контекстах. Зачастую возникает необходимость переопределить значение this . Если вы помните, функции в JavaScript являются объектами. На практике это означает, что у функций есть предопределенные методы. apply() и call() - два из них. Они позволяют переопределять значение this:
    var car = { year: 2008, model: "Dodge Bailout" }; makeArray.apply(car, [ "one", "two" ]); // => [ car, "one", "two" ] makeArray.call(car, "one", "two"); // => [ car, "one", "two" ]
    Эти два метода очень похожи. Первый параметр переопределяет this . Различия между ними заключаются в последющих аргументах: Function.apply() принимает массив значений, которые будут переданы функции, а Function.call() принимает аргументы раздельно. На практике, по моему мнению, удобнее применять apply() .

    Правило вызова функций №3: Если требуется переопределить значение this , не копируя функцию в другой объект, можно использовать myFunction.apply(obj) или myFunction.call(obj) .

    Конструкторы Я не буду подробно останавливаться на объявлении собственных типов в JavaScript, но считаю необходимым напомнить, что в JavaScript нет классов, а любой пользовательский тип нуждается в конструкторе. Кроме того, методы пользовательского типа лучше объявлять через prototype , который является свойством фукции-конструктора. Давайте создадим свой тип:
    // объявляем конструктор function ArrayMaker(arg1, arg2) { this.someProperty = "неважно"; this.theArray = [ this, arg1, arg2 ]; } // объявляем методы ArrayMaker.prototype = { someMethod: function () { alert("Вызван someMethod"); }, getArray: function () { return this.theArray; } }; var am = new ArrayMaker("one", "two"); var other = new ArrayMaker("first", "second"); am.getArray(); // => [ am, "one", "two" ]
    Важным в этом примере является наличие оператора new перед вызовом функции. Если бы не он, это был бы глобальный вызов, и создаваемые в конструкторе свойства относились бы к глобальному объекту. Нам такого не надо. Кроме того, в конструкторах обычно не возвращают значения явно. Без оператора new конструктор вернул бы undefined , с ним он возвращает this . Хорошим стилем считается наименование конструкторов с заглавной буквы; это позволит вспомнить о необходимости оператора new .

    В остальном, код внутри конструктора, скорее всего, будет похож на код, который вы написали бы на другом языке. Значение this в данном случае - это новый объект, который вы создаете.

    Правило вызова функций №4: При вызове функции с оператором new , значением this будет новый объект, созданный средой исполнения JavaScript. Если эта функция не возвращает какой-либо объект явно, будет неявно возвращен this .

    Заключение Надеюсь, понимание разницы между разными способами вызова функций возволит вам улучшить ваш JavaScript-код. Иногда непросто отловить ошибки, связанные со значением this , поэтому имеет смысл предупреждать их возникновение заранее.

    © 2024 beasthackerz.ru - Браузеры. Аудио. Жесткий диск. Программы. Локальная сеть. Windows