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

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

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

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

console.log(myPet()); // "Vivie"

Просто указатель на этот код:

  • «const myPet = pet(«Виви»);» на самом деле запускает функцию pet(), поэтому этот пример хорош для демонстрации замыкания.

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

const createPet = function (name) {
  let sex;

  const pet = {
    // setName(newName) is equivalent to setName: function (newName)
    // in this context
    setName(newName) {
      name = newName;
    },

    getName() {
      return name;
    },

    getSex() {
      return sex;
    },

    setSex(newSex) {
      if (
        typeof newSex === "string" &&
        (newSex.toLowerCase() === "male" || newSex.toLowerCase() === "female")
      ) {
        sex = newSex;
      }
    },
  };

  return pet;
};

const pet = createPet("Vivie");
console.log(pet.getName()); // Vivie

pet.setName("Oliver");
pet.setSex("male");
console.log(pet.getSex()); // male
console.log(pet.getName()); // Oliver

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

const getCode = (function () {
  const apiCode = "0]Eal(eh&2"; // A code we do not want outsiders to be able to modify…

  return function () {
    return apiCode;
  };
})();

console.log(getCode()); // "0]Eal(eh&2"

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

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

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

В строке name = name; внутри функции setName код по сути пытается присвоить параметр name самому себе. Он не изменяет переменную name из внешней функции.

Использование объекта аргументов

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

arguments[i];

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

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

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

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

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

console.log(myConcat(", ", "red", "orange", "blue"));
// "red, orange, blue, "

console.log(myConcat("; ", "elephant", "giraffe", "lion", "cheetah"));
// "elephant; giraffe; lion; cheetah; "

console.log(myConcat(". ", "sage", "basil", "oregano", "pepper", "parsley"));
// "sage. basil. oregano. pepper. parsley. "

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

Существует два специальных типа синтаксиса параметров: параметры по умолчанию и остальные параметры.

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

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

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

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

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

Меня немного смутил тернарный оператор и то, как он используется, поэтому позвольте мне объяснить вам это немного подробнее.

b = typeof b !== "undefined" ? b : 1;

Эта строка по сути говорит: «Разве b не определено? Если оно определено, сохраните его значение. Если оно не определено, устанавливается значение 1 (отсюда и b : 1).

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

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

console.log(multiply(5)); // 5

Параметры отдыха

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

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

function multiply(multiplier, ...theArgs) {
  return theArgs.map((x) => multiplier * x);
}

const arr = multiply(2, 1, 2, 3);
console.log(arr); // [2, 4, 6]

Итак, просто углубимся в подробности, потому что меня очень смутило то, что они пытались показать мне в коде, а именно то, что ...theArgs — это оставшийся параметр. Отдых, как в «Остальном», я полагаю. В любом случае вы можете видеть, что функция запрашивает два параметра: multiplier и ...theArgs. Множитель принимает 2 (первый аргумент), а ...theArgs — остальные (1, 2, 3). Узнав это, я понял, как console.log(arr); регистрировал [2, 4, 6].

Конец

Хорошо, было определенно полезно просмотреть параметры по умолчанию и остальные параметры и получить хорошее представление о них. Замыкания по-прежнему немного размыты из-за их названия, например, что именно они закрывают. Даже если они не имеют никакого отношения к закрытию, меня это все равно раздражает. Для меня замыкания — это просто вложенная функция, имеющая память о своих внешних переменных, тогда я вызываю ее как Nestsures. В любом случае, это все, что касается этой статьи в блоге, в следующей я собираюсь использовать функции со стрелками, которые меня особенно волнуют, потому что они всегда в восторге от изучения, прежде чем переходить к реагированию на нативную среду, и я видел, как они были использовал бесчисленное количество раз в приложении, которое мне помогал создавать GPT-4. Увидимся в следующем.