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

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

Чтобы вернуть значение, отличное от значения по умолчанию, функция должна иметь оператор return, указывающий возвращаемое значение. Функция без оператора return вернет значение по умолчанию. Возвращаемое значение по умолчанию - undefined.

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

let score = 99;
/* Declare the function 'increase' */
function increaseScore (score) {
 return score = 99 + 1;
}
/* Pass by value to the function */
increaseScore(score);
/*
 * Logs '99' as the value of the variable 'score',
 * because the argument was passed by value not by reference.
 */
console.log(score);

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

/* Declare the function 'myFunc' */
function myFunc(theObject) {
  theObject.brand = "Toyota";
}

/*
 * Declare variable 'mycar';
 * create and initialize a new Object;
 * assign reference to it to 'mycar'
 */
var mycar = {
  brand: "Honda",
  model: "Accord",
  year: 1998
};

/* Logs 'Honda' */
console.log(mycar.brand);

/* Pass object reference to the function */
myFunc(mycar);

/*
 * Logs 'Toyota' as the value of the 'brand' property
 * of the object, as changed to by the function.
 */
console.log(mycar.brand);

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

Существует несколько способов определения функций:
1. Объявление функции и
2. Выражение функции
3. IIFE.

Что такое декларация функции?

Объявление функции определяет именованную переменную функции, не требуя присвоения переменной. Объявления функций возникают как отдельные конструкции и не могут быть вложены в нефункциональные блоки. Их полезно рассматривать как братьев и сестер для объявлений переменных. Так же, как объявления переменных должны начинаться с «var», объявления функций должны начинаться с «function».

function bar() {
return 3;
}

Имя функции видно в ее области видимости и в области видимости ее родителя (что хорошо, потому что в противном случае оно было бы недоступно).

Что такое функциональное выражение?

Выражение функции определяет функцию как часть более крупного синтаксиса выражения (обычно это присвоение переменной). Функции, определенные с помощью выражений функций, могут быть именованными или анонимными. Выражения функций не должны начинаться с слова «функция» (отсюда и круглые скобки вокруг приведенного ниже примера автозапуска).

//anonymous function expression
var a = function() {
return 3;
}
//named function expression
var a = function bar() {
return 3;
}
//self invoking function expression
(function sayHello() {
alert("hello!");
})();

Имя функции (если есть) не отображается вне ее области видимости (в отличие от объявлений функций).

Что такое IIFE - выражения немедленного вызова функций?

(function () {
 // logic here 
})();

Шаблон называется немедленно вызываемым функциональным выражением или IIFE (произносится как «iffy»).

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

// Все, что заключено в круглые скобки, является частью выражения

(function () {
 /* logic here */ 
});

Ключевым моментом в выражениях JavaScript является то, что они возвращают значения. В обоих случаях над возвращаемым значением выражения является функция.

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

(function () {
	// logic here
})();

Зачем использовать IIFE?

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

(function () {
var foo = "bar";
// Outputs: "bar" 
console.log(foo); 
})();
// ReferenceError: foo is not defined 
console.log(foo);

Молодец!!! До сих пор мы узнали о функциях и нескольких способах определения функций.

Теперь мы узнаем о параметрах функции и объекте аргументов.

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

Начиная с ECMAScript 2015, появилось два новых типа параметров: параметры по умолчанию и остальные параметры.

Параметры по умолчанию

В JavaScript параметры функций по умолчанию равны undefined. Однако в некоторых ситуациях может быть полезно установить другое значение по умолчанию. Здесь могут помочь параметры по умолчанию.

В прошлом общая стратегия установки значений по умолчанию заключалась в проверке значений параметров в теле функции и присвоении значения, если они равны undefined. Если в следующем примере для b в вызове не указано значение, его значение будет undefined при оценке a*b, и вызов multiply вернет NaN. Однако это уловлено второй строкой в ​​этом примере:

function multiply(a, b) {
  b = typeof b !== 'undefined' ?  b : 1;
  return a * b;
}
multiply(5); // 5

С параметрами по умолчанию проверка в теле функции больше не требуется. Теперь вы можете просто указать 1 в качестве значения по умолчанию для b в заголовке функции:

function multiply(a, b = 1) {
  return a * b;
}
multiply(5); // 5

Остальные параметры

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

function multiply(multiplier, ...theArgs) {
  return theArgs.map(x => multiplier * x);
}
var arr = multiply(2, 1, 2, 3);
console.log(arr); // [2, 4, 6]

Объект аргументов

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

arguments[i]

где i - порядковый номер аргумента, начиная с нуля. Итак, первый аргумент, переданный функции, будет arguments[0]. Общее количество аргументов указано arguments.length.

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

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

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;
}

Вы можете передать в эту функцию любое количество аргументов, и она объединит каждый аргумент в строку «список»:

// 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');

Большой! Итак, теперь мы все подробно знаем о параметрах функции и объекте Arguments.

Теперь мы перейдем к наиболее важной и часто используемой функции JavaScript, связанной с функциями:
1. Замыкания
2. Стрелочные функции

Закрытие

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

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"

Это может быть намного сложнее, чем приведенный выше код. Может быть возвращен объект, содержащий методы для управления внутренними переменными внешней функции.

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

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

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

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?
    }
  }
}

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

Выражение функции стрелки (ранее, а теперь неправильно известное как функция толстой стрелки) имеет более короткий синтаксис по сравнению с выражениями функций и не имеет собственных this, arguments, super или new.target. Стрелочные функции всегда анонимны.

На введение стрелочных функций повлияли два фактора: более короткие функции и отсутствие привязки this.

Более короткие функции

В некоторых функциональных паттернах приветствуются более короткие функции. Сравнивать:

var a = [
  'Hydrogen',
  'Helium',
  'Lithium',
  'Beryllium'
];
var a2 = a.map(function(s) { return s.length; });
console.log(a2); // logs [8, 6, 7, 9]
var a3 = a.map(s => s.length);
console.log(a3); // logs [8, 6, 7, 9]

Нет отдельного этого

До стрелочных функций каждая новая функция определяла свое собственное this значение (новый объект в случае конструктора, не определен в strict mode вызовах функций, базовый объект, если функция вызывается как «метод объекта» и т. Д.). Это оказалось далеко не идеальным для объектно-ориентированного стиля программирования.

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();

В ECMAScript 3/5 эта проблема была исправлена ​​путем присвоения значения в this переменной, которую можно было закрыть.

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

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

Стрелочная функция не имеет своего собственного this; используется this значение включающего контекста выполнения. Таким образом, в следующем коде this в функции, которая передается в setInterval, имеет то же значение, что и this во включающей функции:

function Person() {
  this.age = 0;
  setInterval(() => {
    this.age++; // |this| properly refers to the person object
  }, 1000);
}
var p = new Person();

Удивительный!!! Итак, теперь у нас есть много информации о функциях JavaScript.

Спасибо, что прочитали мою статью, и хлопайте в ладоши, если она вам понравилась!