Недавно я столкнулся с интересным вопросом кода, касающимся замыканий в JavaScript, ответ на который я едва запомнил и определенно никогда не использовал. Это выглядело так:
multiply3Numbers(2)(3)(4) // write this function so that it returns the product of the 3 arguments given
Сначала я начал писать рекурсивную функцию, которая принимала любые существующие дополнительные аргументы. Это стало очень запутанным, и поэтому я подумал, что, возможно, поскольку функция указывает, что она умножает «3» числа, я просто сделаю еще две функции. Когда я писал две другие функции, я был немного озадачен тем, как я смогу отслеживать продукт. Буду ли я передавать переменную продукта из первой функции в следующую, а затем из этой в последнюю, третью функцию? Это не сработало бы, поскольку решение требовало, чтобы каждая функция принимала только один аргумент.
Я решил провести небольшое исследование Google, чтобы найти возможные решения этого вопроса. Именно тогда я наткнулся на интересную особенность JavaScript, связанную с множественными замыканиями.
По сути, решение этой проблемы заключается не в определении функций вне самой верхней функции, а в определении их внутри самой этой функции, что позволяет им иметь доступ к переменным, объявленным в этой самой верхней функции. Конечный результат выглядит так:
function multiply3Numbers(a) { var product = a function multiplyBySecond(b) { product *= b return multiplyByThird } function multiplyByThird(c) { product *= c return product } return multiplyBySecond }
При этом вы можете видеть каждый шаг функции, а также ее замыкания. Когда вы впервые вызываете multiply3Numbers
, вы даете ему начальный аргумент a
. Затем этому аргументу присваивается переменная product
. Возврат этой исходной функции дает вам multiplyBySecond
, который ожидает второй аргумент. Если вы посмотрите на консоль, то увидите, что:
multiply3Numbers(2) // returns the function multiplyBySecond
Поскольку multiply3Numbers
выводит другую функцию, мы можем затем вызвать эту функцию, передав ей аргумент:
multiply3Numbers(2)(3) // returns the function multiplyByThird
Теперь, как мы видели при определении функции multiplyBySecond
выше, она знает переменную product
из своей родительской функции по области видимости (которая инициализируется аргументом, переданным multiply3Numbers
), и умножает это произведение на аргумент, переданный multiplyBySecond
. Итак, прямо сейчас product
равно 2 * 3 = 6. Однако multiplyBySecond
возвращает multiplyByThird
. Когда мы вызываем следующую функцию в цепочке, multiplyByThird
, мы еще раз умножаем product
на заданный аргумент.
multiply3Numbers(2)(3)(4) // returns 24
multiplyByThird
теперь возвращает product
, что равно 24. Эта цепочка работает только потому, что две внутренние функции были определены в этой основной функции, и поэтому сохраняется переменная product
. Тогда нет необходимости передавать product
этим функциям в качестве аргумента, и решение будет достигнуто.