Функции JavaScript

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

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

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

function hello() {
    console.log("Hello mirjs.uz");
}

Данная функция называется hello(). Она не принимает никаких параметров и все, что она делает, это выводит на консоль браузера строку "Hello mirjs.uz".
Чтобы функция выполнила свою работу, нам надо ее вызвать. Общий синтаксис вызова функции:

имя_функции(параметры)

При вызове после имени вызываемой функции в скобках указывается список параметров. Если функция не имеет параметров, то указывются пустые скобки.
Например, определим и вызовем простейшую функцию:

// определение функции
function hello(){
    console.log("Hello mirjs.uz");
}
// вызов функции
hello();

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

Отличительной чертой функций является то, что их можно многократно вызывать в различных местах программы:

// определение функции
function hello(){
    console.log("Hello mirjs.uz");
}
// вызов функции
hello();
hello();
hello();

Переменные и константы в качестве функций

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

// определение функции
function hello() {
    console.log("Hello from mirjs.uz");
}
// передача константе message ссылки на функцию hello
const message = hello;
message();  // вызываем функцию, ссылка на которую хранится в константе message

Присвоив константе или переменной функцию:

const message = hello;

затем мы можем по имени константы/переменной вызывать эту функцию:

message();

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

function goodMorning(){
    console.log("Доброе утро");
}
function goodEvening(){
    console.log("Добрый вечер");
}
let message = goodMorning;      // присваиваем переменной message функцию goodMorning
message();      // Доброе утро
message = goodEvening;          // меняем функцию в переменной message
message();      // Добрый вечер

Анонимные функции

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

let message = function() {
    console.log("Hello JavaScript");
}
message();

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

Функция в JavaScript может принимать параметры. Параметры представляют способ передачи в функцию данных. Параметры указываются в скобках после названия функции.
Например, определим простейшую функцию, которая принимает один параметр:

function print(message) {
console.log(message);
}

print("Hello JavaScript");
print("Hello mirjs.uz");
print("Function in JavaScript");

Функция print() принимает один параметр - message. Поэтому при вызове функции мы можем передать для него значение, например, некоторую строку:
print("Hello JavaScript");
Передаваемые параметрам значения еще называют аргументами.
При этом в отличие от ряда других языков программирования мы в принципе можем не передавать значения параметрам. Например:

function print(message){
console.log(message);
}
print();

Если параметру не передается значение, тогда он будет иметь значение undefined.
Если функция принимает несколько параметров, то они перечисляются через запятую:

function sum(a, b) {
const result = a + b;
console.log(result);
}

sum(2, 6); // 8
sum(4, 5); // 9
sum(109, 11); // 120

При вызове функции с несколькими параметрами значения передаются параметрам по позиции. То есть первое значение передается первому параметру, второе значение - второму и так далее. Например, в вызове:
sum(2, 6);
Число 2 передается параметру a, а число 6 - параметру b.

spread-оператор при передаче параметров

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

function sum(a, b, c) {
const d = a + b + c;
console.log(d);
}
sum(1, 2, 3);
const nums = [4, 5, 6];
sum(...nums);

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

Необязательные параметры и значения по умолчанию

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

function sum(x, y){

if(y === undefined) y = 5;
if(x === undefined) x = 8;
const z = x + y;
console.log(z);
}
sum(); // 13
sum(6); // 11
sum(6, 4) // 10

Здесь функция sum() принимает два параметра. При вызове функции мы можем проверить их значения. При этом, вызывая функцию, необязательно передавать для этих параметров значения. Для проверки наличия значения параметров используется сравнение со значением undefined.
Начиная с ES2015/ES6 мы можем напрямую определять для параметров значения по умолчанию:

function sum(x = 8, y = 5){

const z = x + y;
console.log(z);
}
sum(); // 13
sum(6); // 11
sum(6, 4) // 10

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

function sum(x = 8, y = 10 + x){

const z = x + y;
console.log(z);
}
sum(); // 26
sum(6); // 22
sum(6, 4) // 10

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

Массив arguments

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

function sum(){
let result = 0;
for(const n of arguments)
result += n;
console.log(result);
}
sum(6); // 6
sum(6, 4) // 10
sum(6, 4, 5) // 15

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

Неопределенное количество параметров

С помощью spread-оператора мы можем указать, что с помощью параметра можно передать переменное количество значений:

function display(season, ...temps){
console.log(season);
for(index in temps){
console.log(temps[index]);
}
}
display("Весна", -2, -3, 4, 2, 5);
display("Лето", 20, 23, 31);

В данном случае второй параметр ...temps указывает, что вместо него можно передать разное количество значений. В самой функции temps фактически представляет массив переданных значений, которые мы можем получить. При этом несмотря на это, при вызове функции в нее передается не массив, а именно отдельные значения.
Консольный вывод:
Весна
-2
-3
4
2
5
Лето
20
23
31

Функции в качестве параметров

Функции могут выступать в качестве параметров других функций:

function sum(x, y){
return x + y;
}

function subtract(x, y){
return x - y;
}

function operation(x, y, func){

const result = func(x, y);
console.log(result);
}

console.log("Sum");
operation(10, 6, sum); // 16

console.log("Subtract");
operation(10, 6, subtract); // 4

Функция operation принимает три параметра: x, y и func. func - представляет функцию, причем на момент определения operation не важно, что это будет за функция. Единственное, что известно, что функция func может принимать два параметра и возвращать значение, которое затем отображается в консоли браузера. Поэтому мы можем определить различные функции (например, функции sum и subtract в данном случае) и передавать их в вызов функции operation.

Результат функции

Функция может возвращать результат. Для этого используется оператор return, после которого указывается возвращаемое значение:

function sum (a, b) {
    const result = a + b;
    return result;
}

В данном случае функция sum() принимает два параметра и возвращает их сумму. После оператора return идет возвращаемое значение. В данном случае это значение константы result.
После получения результата функции мы можем присвоить его какой-либо другой переменной или константе:

function sum (a, b) {
  return a + b;
}
let num1 = sum(2, 4);
console.log(num1);  // 6
const num2 = sum(6, 34);
console.log(num2);  // 40

Возвращение функции из функции

Одна функция может возвращать другую функцию:

function menu(n){
    if(n==1) return function(x, y){ return x + y;}
    else if(n==2) return function(x, y){ return x - y;}
    else if(n==3) return function(x, y){ return x * y;}
    return function(){ return 0;}
}
const action = menu(1);         // выбираем первый пункт - сложение
const result = action(2, 5);    // выполняем функцию и получаем результат в константу result
console.log(result);            // 7

В данном случае функция menu() в зависимости от переданного в нее значения возвращает одну из трех функций или пустую функцию, которая просто возвращает число 0.
Далее мы вызываем функцию menu и получаем результат этой функции - другую функцию в константу action.

const action = menu(1);

То есть здесь action будет представлять функцию, которая принимает два параметра и возвращает число. Затем черещ имя константы мы можем вызвать эту функцию и получить ее результат в константу result:

const result = action(2, 5);

Подобным образом мы можем получить и другую возвращаемые функции:

function menu(n){
    if(n==1) return function(x, y){ return x + y;}
    else if(n==2) return function(x, y){ return x - y;}
    else if(n==3) return function(x, y){ return x * y;}
    return function(){ return 0;};
}
let action = menu(1);
console.log(action(2, 5));          // 7
action = menu(2);
console.log(action(2, 5));          // -3
action = menu(3);
console.log(action(2, 5));          // 10
action = menu(190);
console.log(action(2, 5));          // 0

Область видимости переменных

Все переменные и константы в JavaScript имеют определенную область видимости, в пределах которой они могут действовать.

Глобальные переменные

Все переменные и константы, которые объявлены вне функций, являются глобальными:

var a = 5;
let b = 8;
const c = 9;
function displaySum(){
    var d = a + b + c;
    console.log(d);
}
displaySum(); // 22

Здесь переменные a и b и константа c являются глобальными. Они доступны из любого места программы.
А вот переменная d глобальной не является, так как она определена внутри функции и видна только в этой функции.

Определение локальной области видимости

Для определения локальной области видимости в JavaScript используются фигурные скобки { }, которые создают блок кода. Этот блок кода может быть безымянным, может быть именнованным, например, функция, либо может представлять условную или циклическую конструкцию. Например, определение переменных в безымянном блоке кода:

{
    var a = 5;
    let b = 8;
    const c = 9;
}

Однако в этом случае поведение переменной зависит от способа ее определения (через var или через let) и от типа блока. var определяет локальные переменные уровня функции, а let определяет локальные переменные уровня блока кода (подобным образом constопределяет константы уровня блока кода). Рассмотрим, в чем состоит отличие.

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

Переменные и константы, определенные внутри функции, видны (то есть могут использоваться) только внутри этой функции:

function print(){
    var a = 5;
    let b = 8;
    const c = 9;
    console.log("Function print: a =", a);
    console.log("Function print: b =", b);
    console.log("Function print: c =", c);
}
print();
console.log("Global: a =", a);  // Uncaught ReferenceError: a is not defined

Переменные a и b и константа c являются локальными, они существуют только в пределах функции. Вне функции их нельзя использовать, поэтому мы получим следующий консольный вывод:
Function print: a= 5
Function print: b= 8
Function print: c= 9
Uncaught ReferenceError: a is not defined
Здесь мы видим, что при попытке обратиться к переменной a вне функции print(), браузер выводит ошибку. При этом подобное поведение не зависит от того, что это за переменная - var или let, либо это константа. Подобное поведение для всех переменных и констант одинаково.

Локальные переменные в блоках кода, условиях и циклах

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

Переменная var

Переменная, объявленная с помощью var, может использоваться вне блока:

// безымянный блок
{
    var a = 5;
}
console.log("a =", a);  // a = 5
// условная конструкция
if(true){
    var b = 6;
}
console.log("b =", b);  // b = 6
// цикл
for(var i = 0; i < 5; i++){     var c = 7; } console.log("c =", c);  // c = 7

Единственное условие, что блок кода должен срабатывать, чтобы инициализировать переменную. Так, в примере выше условие в конструкции if и в цикле for установлено так, что блок этих конструкций будет выполняться. Однако, что если условие будет иным, что блок не будет выполняться?

if(false){
    var b = 6;
}
console.log("b =", b);  // b = undefined
// цикл
for(var i = 1; i < 0; i++){     var c = 7; } console.log("c =", c);  // c = undefined

В таком случае мы опять же сможем обращаться к переменным, только они будут иметь значение undefined.

Переменная let и константы

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

{
    let a = 5;
}
console.log("a =", a);  // Uncaught ReferenceError: a is not defined

В данном случае мы получим ошибку. Мы можем использовать переменные let, определенные внутри блока кода, можно использовать только внутри этого блока кода.
Тоже самое относится и к константам:

{
    const b = 5;
}
console.log("b =", b);  // Uncaught ReferenceError: b is not defined

Сокрытие переменных

Что если у нас есть две переменных - одна глобальная, а другая локальная, которые имеют одинаковое имя:

var z = 89;
function print(){
    var z = 10;
    console.log(z); // 10
}
print(); // 10

В этом случае в функции будет использоваться та переменная z, которая определена непосредственно в функции. То есть локальная переменная скроет глобальную. Однако конкретное поведение при сокрытии зависит от того, как определяется переменная.

Скрытие переменной var

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

function displayZ(){
    var z = 20;
    {
        var z = 30; // Ошибка ! Переменная z уже определена
        console.log("Block:", z);
    }
    console.log("Function:", z);
}

То есть с помощью var мы можем определить переменную с одним именем либо на уровне функции, либо на уровне блока кода.

Скрытие переменной let

Как писалось выше, оператор let определяет переменную уровня блока кода. То есть каждый блок кода определяет новую область видимости, в которой существует переменная. Ве блока кода, где определена переменная, она не существует. Соответственно мы можем одновременно определить переменную на уровне блока и на уровне функции (в отличие от var):

let z = 10;
function displayZ(){
    let z = 20;
    {
        let z = 30;
        console.log("Block:", z);
    }
    console.log("Function:", z);
}
displayZ();
console.log("Global:", z);

Здесь внутри функции displayZ определен блок кода, в котором определена переменная z (вместо безымянного блока это мог быть и блок условной конструкции или цикла). Она скрывает глобальную переменную и переменную z, определенную на уровне функции.
И в данном случае мы получим следующий консольный вывод:
Block: 30
Function: 20
Global: 10

Константы

Все, что относится к оператору let, относится и к оператору const, который определяет константы уровня блока кода. Блоки кода задают область видимости констант, а константы, определенные на вложенных блоках кода, скрывают внешние константы с тем же именем:

const z = 10;
function displayZ(){
    const z = 20;
    {
        const z = 30;
        console.log("Block:", z);   // 30
    }
    console.log("Function:", z);    // 20
}
displayZ();
console.log("Global:", z);  // 10

Необъявленные переменные

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

function bar(){
    foo = "25";
}
bar();
console.log(foo);   // 25

Несмотря на то, что вне функции bar переменная foo нигде не определяется, тем не менее она доступна вне функции во внешнем контексте. Единственное условие - мы вызываем функцию, где определена такая переменная.
Однако если мы не вызовем функцию, переменная будет не определена:

function bar(){
    foo = "25";
}
// bar();   Функция НЕ вызывается
console.log(foo);   // ошибка - Uncaught ReferenceError: foo is not defined

Ту же ошибку мы получили бы, если бы мы не только присваивали значение переменной, но и определяли бы ее:

function bar(){
    var foo = "25";
}
bar();
console.log(foo);   // ошибка - Uncaught ReferenceError: foo is not defined

strict mode

Определение глобальных переменных в функциях может вести к потенциальным ошибкам. Чтобы их избежать используется строгий режим или strict mode:

"use strict";
function bar(){
    foo = "25";     // Uncaught ReferenceError: foo is not defined
}
bar();
console.log(foo);

В этом случае мы получим ошибку SyntaxError: Unexpected identifier, которая говорит о том, что переменная foo не определена.
Установить режим strict mode можно двумя способами:
добавить выражение "use strict" в начало кода JavaScript, тогда strict mode будет применяться для всего кода
добавить выражение "use strict" в начало тела функции, тогда strict mode будет применяться только для этой функции. Пример использования на уровне функции:

function bar(){
    "use strict";
    foo = "25";
}

Замыкания и функции IIFE

Замыкания

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

function outer(){       // внешняя функция
    var n;              // некоторая переменная
    return inner(){     // вложенная функция
        // действия с переменной n
    }
}

Рассмотрим замыкания на простейшем примере:

function outer(){
    let x = 5;
    function inner(){
        x++;
        console.log(x);
    };
    return inner;
}
let fn = outer();   // fn = inner, так как функция outer возвращает функцию inner
// вызываем внутреннюю функцию inner
fn();   // 6
fn();   // 7
fn();   // 8

Здесь функция outer задает область видимости, в которой определены внутренняя функция inner и переменная x. Переменная x представляет лексическое окружение для функции inner. В самой функции inner инкрементируем переменную x и выводим ее значение на консоль. В конце функция outer возвращает функцию inner.
Далее вызываем функцию outer:

let fn = outer();

Поскольку функция outer возвращает функцию inner, то переменная fn будет хранить ссылку на функцию inner. При этом эта функция запомнила свое окружение - то есть внешнюю переменную x.
Далее мы фактически три раза вызываем функцию Inner, и мы видим, что переменная x, которая определена вне функции inner, инкрементируется:

fn();   // 6
fn();   // 7
fn();   // 8

То есть несмотря на то, что переменная x определена вне функции inner, эта функция запомнила свое окружение и может его использовать, несомотря на то, что она вызывается вне функции outer, в которой была определена. В этом и суть замыканий.
Рассмотрим еще один пример:

function multiply(n){
    var x = n;
    return function(m){ return x * m;};
}
var fn1 = multiply(5);
var result1 = fn1(6); // 30
console.log(result1); // 30
var fn2= multiply(4);
var result2 = fn2(6); // 24
console.log(result2); // 24

Итак, здесь вызов функции multiply() приводит к вызову другой внутренней функции. Внутренняя же функция:

function(m){ return x * m;};

function(m){ return x * m;};
запоминает окружение, в котором она была создана, в частности, значение переменной x.
В итоге при вызове функции multiply определяется переменная fn1, которая и представляет собой замыкание, то есть объединяет две вещи: функцию и окружение, в котором функция была создана. Окружение состоит из любой локальной переменной, которая была в области действия функции multiply во время создания замыкания.
То есть fn1 — это замыкание, которое содержит и внутреннюю функцию function(m){ return x * m;}, и переменную x, которая существовала во время создания замыкания.
При создании двух замыканий: fn1 и fn2, для каждого из этих замыканий создается свое окружение.
При этом важно не запутаться в параметрах. При определении замыкания:

var fn1 = multiply(5);

Число 5 передается для параметра n функции multiply.
При вызове внутренней функции:

var result1 = fn1(6);

Число 6 передается для параметра m во внутреннюю функцию function(m){ return x * m;};.
Также мы можем использовать другой вариант для вызова замыкания:

function multiply(n){
    var x = n;
    return function(m){ return x * m;};
}
var result = multiply(5)(6); // 30
console.log(result);

Самовызывающиеся функции

Обычно определение функции отделяется от ее вызова: сначала мы определяем функцию, а потом вызываем. Но это необязательно. Мы также можем создать такие функции, которые будут вызываться сразу при определении. Такие функции еще называют Immediately Invoked Function Expression (IIFE).

(function(){
    console.log("Привет мир");
}());
(function (n) {
    var result = 1;
    for(var i=1; i<=n; i++)         result *=i;     console.log("Факториал числа " + n + " равен " + result); }(4));

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

Паттерн Модуль

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

let foo = (function(){
    let obj = {greeting: "hello"};
    return {
        display: function(){
            console.log(obj.greeting);
        }
    }
})();
foo.display();  // hello

Здесь определена переменная foo, которая представляет результат анонимной функции. Внутри подобной функции определен объект obj с некоторыми данными.
Сама анонимная функция возвращает объект, который определяет функцию display. Возвращаемый объект определяет общедоступый API, через который мы можем обращаться к данным, определенным внутри модуля.

return {
        display: function(){
            console.log(obj.greeting);
        }
    }

Такая конструкция позволяет закрыть некоторый набор данных в рамках функции-модуля и опосредовать доступ к ним через определенный API - возвращаемые внутренние функции.
Рассмотрим чуть более сложный пример:

let calculator = (function(){
    let data = { number: 0};
    return {
        sum: function(n){
            data.number += n;
        },
        subtract: function(n){
            data.number -= n;
        },
        display: function(){
            console.log("Result: ", data.number);
        }
    }
})();
calculator.sum(10);
calculator.sum(3);
calculator.display();   // Result: 13
calculator.subtract(4);
calculator.display();   // Result: 9

Данный модуль представляет примитивный калькулятор, который выполняет три операции: сложение, вычитание и вывод результата.
Все данные инкапсулированы в объекте data, который хранит результат операции. Все операции представлены тремя возвращаемыми функциями: sum, subtract и display. Через эти функции мы можем управлять результатом калькулятора извне.

Рекурсивные функции

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

function getFactorial(n){
    if (n === 1){
        return 1;
    }
    else{
        return n * getFactorial(n - 1);
    }
}
var result = getFactorial(4);
console.log(result); // 24

Функция getFactorial() возвращает значение 1, если параметр n равен 1, либо возвращает результат опять же функции getFactorial, то в нее передается значение n-1. Например, при передаче числа 4, у нас образуется следующая цепочка вызовов:

var result = 4 * getFactorial(3);
var result = 4 * 3 * getFactorial(2);
var result = 4 * 3 * 2 * getFactorial(1);
var result = 4 * 3 * 2 * 1; // 24

Рассмотрим другой пример - определение чисел Фибоначчи:

function getFibonachi(n)
{
    if (n === 0){
        return 0;
    }
    if (n === 1){
        return 1;
    }
    else{
        return getFibonachi(n - 1) + getFibonachi(n - 2);
    }
}
var result = getFibonachi(8); //21
console.log(result); // 21

Переопределение функций

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

function display(){
    console.log("Доброе утро");
    display = function(){
        console.log("Добрый день");
    }
}
display(); // Доброе утро
display(); // Добрый день

При первом срабатывании функции действует основной блок операторов функции, в частности, в данном случае выводится сообщение "Доброе утро". И при первом срабатывании функции display также происходит ее переопределение. Поэтому при всех последующих вызовах функции срабатывает ее переопределенная версия, а на консоль будет выводиться сообщение "Добрый день".
Но при переопределении функции надо учитывать некоторые нюансы. В частности, попробуем присвоить ссылку на функцию переменной и через эту переменную вызвать функцию:

function display(){
    console.log("Доброе утро");
    display = function(){
        console.log("Добрый день");
    }
}
// присвоение ссылки на функцию до переопределения
var displayMessage = display;
display(); // Доброе утро
display(); // Добрый день
displayMessage(); // Доброе утро
displayMessage(); // Доброе утро

Здесь переменная displayMessage получает ссылку на функцию display до ее переопределения. Поэтому при вызове displayMessage() будет вызываться непереопределенная версия функции display.
Но допустим, мы определили переменную displayMessage уже после вызова функции display:

display(); // Доброе утро
display(); // Добрый день
var displayMessage = display;
displayMessage(); // Добрый день
displayMessage(); // Добрый день

В этом случае переменная displayMessage будет указывать на переопределенную версию функции display.

Передача параметров по значению и по ссылке

Передача параметров по значению

Строки, числа, логические значения передаются в функцию по значению. Иными словами при передаче значения в функцию, эта функция получает копию данного значения. Рассмотрим, что это значит в практическом плане:

function change(x){
    x = 2 * x;
    console.log("x in change:", x);
}
var n = 10;
console.log("n before change:", n); // n before change: 10
change(n);                          // x in change: 20
console.log("n after change:", n);  // n after change: 10

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

Передача по ссылке

Объекты и массивы передаются по ссылке. То есть функция получает сам объект или массив, а не их копию.

function change(user){
    user.name = "Tom";
}
var bob ={
    name: "Bob"
};
console.log("before change:", bob.name);    // Bob
change(bob);
console.log("after change:", bob.name);     // Tom

В данном случае функция change получает объект и меняет его свойство name. В итоге мы увидим, что после вызова функции изменился оригинальный объект bob, который передавался в функцию.
Однако если мы попробуем переустановить объект или массив полностью, оригинальное значение не изменится.

function change(user){
    // полная переустановка объекта
    user= {
        name:"Tom"
    };
}
var bob ={
    name: "Bob"
};
console.log("before change:", bob.name);    // Bob
change(bob);
console.log("after change:", bob.name);     // Bob

То же самое касается массивов:

function change(array){
    array[0] = 8;
}
function changeFull(array){
    array = [9, 8, 7];
}
var numbers = [1, 2, 3];
console.log("before change:", numbers);     // [1, 2, 3]
change(numbers);
console.log("after change:", numbers);      // [8, 2, 3]
changeFull(numbers);
console.log("after changeFull:", numbers);  // [8, 2, 3]

Стрелочные функции

Стрелочные функции (arrow functions) позоляют сократить определение обычных функций. Стрелочные функции образуются с помощью знака стрелки (=>), перед которым в скобках идут параметры функции, а после - собственно тело функции.

(параметры) => действия_функции

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

function hello(){
    console.log("Hello");
}
hello();    // вызываем функцию

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

let hello = ()=> console.log("Hello");
hello();

В данном случае стрелочная функция присваивается переменной hello, через которую затем можно вызвать данную функцию.
Здесь мы не используем параметры, поэтому указываются пустые скобки ()=> console.log("Hello");
Далее через имя переменной мы можем вызвать данную функцию.

Передача параметров

Теперь определим стрелочную функцию, которая принимает один параметр:

let print = (mes)=> console.log(mes);
print("Hello mirjs.uz");
print("Welcome to JavaScript");

Здесь стрелочная функция принимает один параметр mes, значение которого выводится на консоль браузера.
Если стрелочная функция имеет только один параметр, то скобки вокруг списка параметров можно опустить:

let print = mes=> console.log(mes);
print("Hello mirjs.uz");
print("Welcome to JavaScript");

Другой пример - передадим два параметра:

let sum = (x, y)=> console.log("Sum =", x + y);
sum(1, 2);      // Sum = 3
sum(4, 3);      // Sum = 7
sum(103, 2);    // Sum = 105

Возвращение результата

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

let sum = (x, y)=> x + y;
console.log(sum(1, 2));     // 3
console.log(sum(4, 3));     // 7
console.log(sum(102, 5));   // 105

Другой пример - возвратим отфарматированную строку:

const hello = name => `Hello, ${name}`;
console.log(hello("Tom"));              // Hello, Tom
console.log(hello("Bob"));              // Hello, Bob
console.log(hello("Frodo Baggins"));    // Hello, Frodo Baggins
В данном случае функция hello принимает один параметр name - условное имя и создает на его основе сообщение с приветствием.

Возвращение объекта

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

let user = (userName, userAge) => ({name: userName, age: userAge});
let tom = user("Tom", 34);
let bob = user("Bob", 25);
console.log(tom.name, tom.age);     // "Tom", 34
console.log(bob.name, bob.age);     // "Bob", 25

Объект также определяется с помощью фигурных скобок, но при этом он заключается в круглые скобки.

Функция из нескольких инструкций

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

const square = n => {
    let result = n * n;
    console.log(result);
}
square(5);     // 25
square(6);     // 36

А если надо возвратить результат, применяется оператор return, как в обычной функции:

const square = n => {
    let result = n * n;
    return result;
}
console.log(square(5));     // 25

Обсуждение закрыто.