Замыкания, функции изнутри
Материал на этой странице устарел, поэтому скрыт из оглавления сайта.
Более новая информация по этой теме находится на странице https://learn.javascript.ru/closure.
В этой главе мы продолжим рассматривать, как работают переменные, и, как следствие, познакомимся с замыканиями. От глобального объекта мы переходим к работе внутри функций.
Лексическое окружение
Все переменные внутри функции – это свойства специального внутреннего объекта LexicalEnvironment , который создаётся при её запуске.
Мы будем называть этот объект «лексическое окружение» или просто «объект переменных».
При запуске функция создаёт объект LexicalEnvironment , записывает туда аргументы, функции и переменные. Процесс инициализации выполняется в том же порядке, что и для глобального объекта, который, вообще говоря, является частным случаем лексического окружения.
В отличие от window , объект LexicalEnvironment является внутренним, он скрыт от прямого доступа.
Пример
Посмотрим пример, чтобы лучше понимать, как это работает:
При вызове функции:
До выполнения первой строчки её кода, на стадии инициализации, интерпретатор создаёт пустой объект LexicalEnvironment и заполняет его.
В данном случае туда попадает аргумент name и единственная переменная phrase :
Во время выполнения происходит присвоение локальной переменной phrase , то есть, другими словами, присвоение свойству LexicalEnvironment.phrase нового значения:
В конце выполнения функции объект с переменными обычно выбрасывается и память очищается. В примерах выше так и происходит. Через некоторое время мы рассмотрим более сложные ситуации, при которых объект с переменными сохраняется и после завершения функции.
Если почитать спецификацию ECMA-262, то мы увидим, что речь идёт о двух объектах: VariableEnvironment и LexicalEnvironment .
Но там же замечено, что в реализациях эти два объекта могут быть объединены. Так что мы избегаем лишних деталей и используем везде термин LexicalEnvironment , это достаточно точно позволяет описать происходящее.
Более формальное описание находится в спецификации ECMA-262, секции 10.2-10.5 и 13.
Доступ ко внешним переменным
Из функции мы можем обратиться не только к локальной переменной, но и к внешней:
Интерпретатор, при доступе к переменной, сначала пытается найти переменную в текущем LexicalEnvironment , а затем, если её нет – ищет во внешнем объекте переменных. В данном случае им является window .
Такой порядок поиска возможен благодаря тому, что ссылка на внешний объект переменных хранится в специальном внутреннем свойстве функции, которое называется [[Scope]] . Это свойство закрыто от прямого доступа, но знание о нём очень важно для понимания того, как работает JavaScript.
При создании функция получает скрытое свойство [[Scope]] , которое ссылается на лексическое окружение, в котором она была создана.
В примере выше таким окружением является window , так что создаётся свойство:
Это свойство никогда не меняется. Оно всюду следует за функцией, привязывая её, таким образом, к месту своего рождения.
При запуске функции её объект переменных LexicalEnvironment получает ссылку на «внешнее лексическое окружение» со значением из [[Scope]] .
Если переменная не найдена в функции – она будет искаться снаружи.
Именно благодаря этой механике в примере выше alert(userName) выводит внешнюю переменную. На уровне кода это выглядит как поиск во внешней области видимости, вне функции.
- Каждая функция при создании получает ссылку [[Scope]] на объект с переменными, в контексте которого была создана.
- При запуске функции создаётся новый объект с переменными LexicalEnvironment . Он получает ссылку на внешний объект переменных из [[Scope]] .
- При поиске переменных он осуществляется сначала в текущем объекте переменных, а потом – по этой ссылке.
Выглядит настолько просто, что непонятно – зачем вообще говорить об этом [[Scope]] , об объектах переменных. Сказали бы: «Функция читает переменные снаружи» – и всё. Но знание этих деталей позволит нам легко объяснить и понять более сложные ситуации, с которыми мы столкнёмся далее.
Всегда текущее значение
Значение переменной из внешней области берётся всегда текущее. Оно может быть уже не то, что было на момент создания функции.
Например, в коде ниже функция sayHi берёт phrase из внешней области:
На момент первого запуска (*) , переменная phrase имела значение ‘Привет’ , а ко второму (**) изменила его на ‘Пока’ .
Это естественно, ведь для доступа к внешней переменной функция по ссылке [[Scope]] обращается во внешний объект переменных и берёт то значение, которое там есть на момент обращения.
Вложенные функции
Внутри функции можно объявлять не только локальные переменные, но и другие функции.
К примеру, вложенная функция может помочь лучше организовать код:
Здесь, для удобства, создана вспомогательная функция getFullName() .
Вложенные функции получают [[Scope]] так же, как и глобальные. В нашем случае:
Благодаря этому getFullName() получает снаружи firstName и lastName .
Заметим, что если переменная не найдена во внешнем объекте переменных, то она ищется в ещё более внешнем (через [[Scope]] внешней функции), то есть, такой пример тоже будет работать:
Возврат функции
Рассмотрим более «продвинутый» вариант, при котором внутри одной функции создаётся другая и возвращается в качестве результата.
В разработке интерфейсов это совершенно стандартный приём, функция затем может назначаться как обработчик действий посетителя.
Здесь мы будем создавать функцию-счётчик, которая считает свои вызовы и возвращает их текущее число.
В примере ниже makeCounter создаёт такую функцию:
Как видно, мы получили два независимых счётчика counter и counter2 , каждый из которых незаметным снаружи образом сохраняет текущее количество вызовов.
Где? Конечно, во внешней переменной currentCount , которая у каждого счётчика своя.
Если подробнее описать происходящее:
В строке (*) запускается makeCounter() . При этом создаётся LexicalEnvironment для переменных текущего вызова. В функции есть одна переменная var currentCount , которая станет свойством этого объекта. Она изначально инициализуется в undefined , затем, в процессе выполнения, получит значение 1 :
В процессе выполнения makeCounter() создаёт функцию в строке (**) . При создании эта функция получает внутреннее свойство [[Scope]] со ссылкой на текущий LexicalEnvironment .
Далее вызов makeCounter() завершается и функция (**) возвращается и сохраняется во внешней переменной counter (*) .
На этом создание «счётчика» завершено.
Итоговым значением, записанным в переменную counter , является функция:
Возвращённая из makeCounter() функция counter помнит (через [[Scope]] ) о том, в каком окружении была создана.
Это и используется для хранения текущего значения счётчика.
Далее, когда-нибудь, функция counter будет вызвана. Мы не знаем, когда это произойдёт. Может быть, прямо сейчас, но, вообще говоря, совсем не факт.
Эта функция состоит из одной строки: return currentCount++ , ни переменных ни параметров в ней нет, поэтому её собственный объект переменных, для краткости назовём его LE – будет пуст.
Однако, у неё есть свойство [[Scope]] , которое указывает на внешнее окружение. Чтобы увеличить и вернуть currentCount , интерпретатор ищет в текущем объекте переменных LE , не находит, затем идёт во внешний объект, там находит, изменяет и возвращает новое значение:
Переменную во внешней области видимости можно не только читать, но и изменять.
В примере выше было создано несколько счётчиков. Все они взаимно независимы:
Они независимы, потому что при каждом запуске makeCounter создаётся свой объект переменных LexicalEnvironment , со своим свойством currentCount , на который новый счётчик получит ссылку [[Scope]] .
Свойства функции
Функция в JavaScript является объектом, поэтому можно присваивать свойства прямо к ней, вот так:
Свойства функции не стоит путать с переменными и параметрами. Они совершенно никак не связаны. Переменные доступны только внутри функции, они создаются в процессе её выполнения. Это – использование функции «как функции».
А свойство у функции – доступно отовсюду и всегда. Это – использование функции «как объекта».
Если хочется привязать значение к функции, то можно им воспользоваться вместо внешних переменных.
В качестве демонстрации, перепишем пример со счётчиком:
При запуске пример работает также.
Принципиальная разница – во внутренней механике и в том, что свойство функции, в отличие от переменной из замыкания – общедоступно, к нему имеет доступ любой, у кого есть объект функции.
Например, можно взять и поменять счётчик из внешнего кода:
Иногда свойства, привязанные к функции, называют «статическими переменными».
В некоторых языках программирования можно объявлять переменную, которая сохраняет значение между вызовами функции. В JavaScript ближайший аналог – такое вот свойство функции.
Итого: замыкания
Замыкание – это функция вместе со всеми внешними переменными, которые ей доступны.
Таково стандартное определение, которое есть в Wikipedia и большинстве серьёзных источников по программированию. То есть, замыкание – это функция + внешние переменные.
Тем не менее, в JavaScript есть небольшая терминологическая особенность.
Обычно, говоря «замыкание функции», подразумевают не саму эту функцию, а именно внешние переменные.
Иногда говорят «переменная берётся из замыкания». Это означает – из внешнего объекта переменных.
Иногда говорят «Вася молодец, понимает замыкания!». Что это такое – «понимать замыкания», какой смысл обычно вкладывают в эти слова?
«Понимать замыкания» в JavaScript означает понимать следующие вещи:
- Все переменные и параметры функций являются свойствами объекта переменных LexicalEnvironment . Каждый запуск функции создаёт новый такой объект. На верхнем уровне им является «глобальный объект», в браузере – window .
- При создании функция получает системное свойство [[Scope]] , которое ссылается на LexicalEnvironment , в котором она была создана.
- При вызове функции, куда бы её ни передали в коде – она будет искать переменные сначала у себя, а затем во внешних LexicalEnvironment с места своего «рождения».
В следующих главах мы углубим это понимание дополнительными примерами, а также рассмотрим, что происходит с памятью.
Подробно о методах apply(), call() и bind(), необходимых каждому JavaScript разработчику
![]()
Вообще, эти методы довольно старые, но их понимание не становится от этого менее необходимым при изучении JavaScript. В этой статье вы узнаете о том, как работают apply() , call() и bind() , а также поймете на реальных примерах, что они на самом делают и как смогут вам пригодится в реальной жизни.
Мой Твиттер — там много из мира фронтенда, да и вообще поговорим. Подписывайтесь, будет интересно: ) ✈️
В JavaScript функции это объекты, как вы должны были бы уже знать. И как объекты, функции имеют свои методы, включая такие действенные, как apply() , call() и bind() . Можно сказать, что Apply() и Call() буквально идентичны друг другу и зачастую используются в JavaScript для того, чтобы заимствовать методы и выставлять значения this . Также мы используем Apply() для функций с большим количеством переменных и аргументов, но об этом вы узнаете дальше в статье.
Bind() же мы используем для выставления значения this в методах и для каррирования функций.
Мы рассмотрим каждый сценарий, в котором будет использоваться каждый из трех методов. Давайте начнем с метода bind() .
Метод bind()
В основном, мы используем метод bind() , чтобы вызывать функцию с указанием значения this . А другими словами, bind() позволяет нам легко выставлять какой именно объект будет привязан к this в момент вызова функции или метода.
Это может показаться относительно тривиальным, но зачастую значение this в методах и функциях должно быть выставлено явным способом, когда вам нужно привязать конкретный объект к значению this самой функции.
Обычно нам требуется bind() тогда, когда мы используем в методе this и вызываем сам метод из получающего объекта. В таких случаях, иногда this не привязывается к предполагаемому объекту, что само собой ведет к ошибке в работе кода. Так, сейчас без паники, если вы полностью не поняли предыдущее предложение. Всё скоро станет кристально ясным. За этим вы и читаете эту статью.
Перед тем, как взглянуть на код ниже, вы должны понять то, как работает this в JavaScript. Для этого прочтите статью — Подробно о том, как работает this в JavaScript. Если вы не будете понимать его работы, то вы встретите множество затруднений в понимании концепций, описанных ниже. Так что, лучше не ленитесь и прочтите её, потратьте время, оно вам окупится. А что будет ещё лучше, бонусом прочтите Разбираемся с “поднятием” (hoisting) в JavaScript
Bind() позволяет нам выставить значение this для метода
При клике на кнопку ниже, текстовое поле будет заполнено рандомным именем.
Итак, когда вы кликаете на кнопку, вы получаете ошибку, так как clickHandler() метод привязан к HTML элементу — кнопке, следовательно, он считается объектом на котором выполняется метод.
Вообще, эта проблема довольно распространена в JavaScript и фреймворки, такие как Backbone.js, вместе с библиотеками типа jQuery автоматически делают привязывание за нас, таким образом, что this всегда будет связан с объектом, работу с которым мы подразумеваем в нашем коде.
Чтобы исправить эту проблему в предыдущем примере, мы можем использовать метод bind() :
Следовательно, вместо этой строки:
Мы просто свяжем clickHandler с объектом user , как тут:
Давайте продолжим. Значение this , также привязывается к другому объекту, если мы назначаем метод (где this определено) на переменную. В этом примере вы это увидите:
Когда мы выполняем функцию showDataVar() , значения, выводимые в консоль идут из глобального data массива, а не из data массива внутри объекта user . Так получается, потому что showDataVar() выполняется как глобальная функция и использование this внутри showDataVar() привязано к глобальному полю видимости, что и является объектом window самого браузера.
И снова, мы можем пофиксить эту проблему, указав точно значения this c помощью метода bind() .
С помощью bind() мы можем заимствовать методы
В JavaScript мы можем передавать функции куда угодно, возвращать их, а ещё заимствовать и всё тому подобное. А с помощью метода bind() это будет просто ну супер как легко сделать и в особенности это касается заимствования методов.
Вот пример использования bind() , чтобы позаимствовать метод:
Тут проблема заключается в том, что мы добавляем новый метод ( showData ) объекту cars и возможно нам просто не нужно его заимствовать, потому что сам объект уже может иметь свойство или метод под названием showData . В общем, мы не хотим его случайно переписать. Как будет показано далее, на примерах Apply() и Call() , лучше всего заимствовать методы используя либо Apply() или Call() .
С помощью bind() мы можем каррировать функцию
Каррирование функции, также известное, как частичное применение функции — это использование функции, которая возвращает новую функцию с уже частично выставленными аргументами. Эта функция имеет доступ к хранящимся аргументам и переменным внешней функции. В общем, это звучит куда сложнее, чем есть на самом деле.
Давайте уже применим метод bind() и каррирование вместе. Для начала, у нас есть простенькая функция greet() , которая принимает 3 параметра:
И мы используем bind() метод, чтобы каррировать нашу функцию greet() . Как мы говорили ранее, первый аргумент метода bind() будет иметь значение this .
Используя метод bind() для каррировния, все наши параметры greet() , кроме последнего аргумента, подставляются автоматически. Таким образом этот аргумент, который мы меняем, используется при вызове новых функций, каррированных с greet() .
В общем, с помощью метода bind() , мы можем выставлять значение для вызова методов на объекте, заимствовать и копировать методы, а также назначать методы переменным, которые выполнятся как функции.
Методы Apply и Call
Два этих метода чуть ли не самые используемые в JavaScript и это неспроста: с их помощью мы можем заимствовать функции и выставлять значение this в вызове функции. Более того, функция apply() в буквальном смысле позволяет нам выполнять функцию в массиве параметров, как-будто каждый параметр передаётся функции индивидуально, при её выполнении — отличное решение для вариативных функций; такие функции берут варьирующееся число аргументов, а не заданное количество, как делается в большинстве функций.
Выставляем значение this с помощью Apply или Call
Как и в примере с bind() , мы также можем указывать значение для this, когда вызываем функцию используя методы apply() и call() . Первый параметр в call() и apply() выставляет this значение объекту на котором вызвана функция.
Вот очень быстрый и показательный пример для начинающих, перед тем, как мы окунёмся в использование Apply и Call:
А сейчас очень внимательно! Обратите внимание, что первый аргумент в call() выставляет значение this . В предыдущем примере, оно выставляется на объект gameController . А другие аргументы, после первого, передаются как параметры функции avg() .
Методы apply() и call() практически идентичны при работе с выставлением значения this , за исключением того, что вы передаёте параметры функции в apply() как массив, в то время, как в call() , параметры передаются в индивидуальном порядке. Но дальше ещё интереснее. У apply() есть ещё одна фича, которой нет у call() и очень скоро вы о ней узнаете.
Используем call() и apply(), чтобы выставлять this в Callback функциях
Сама функция, тут очень внимательно:
Метод apply() выставляет значение this для callbackObj . Так мы можем выполнить колбэк с указанным значением this , так что параметры переданные колбэку, будут выставлены на объекте clientData :
Методы apply() , call() и bind() всегда используются для того, чтобы выставлять значение this , при вызове метода, но каждый слегка по-своему и это отображается на том, как мы можем контролировать и поддерживать наш код. Значение this в JavaScript также важно, как и любая другая часть языка. Помните, что у нас есть 3 вышеупомянутых метода, которые просто необходимы для эффективной и правильной работы с этим самым this . Всегда старайтесь перепроверять то, что предполагается на месте this .
Заимствование функций с помощью Apply и Call (Важно знать)
Вообще Apply() и Call() в JavaScript используют в основном, чтобы заимствовать функции. С этими методами, мы можем выполнять заимствования также, как и с методом bind() , но при этом быть более гибкими. Давайте рассмотрим следующие примеры:
Заимствуем методы массива
У массивов есть несколько полезных методов для перебора и изменения массива, но к сожалению, у объектов нет стольких методов по-дефолту. Однако, мы можем выражать объект как массив (в виде массивоподобного или как иногда говорят — итерируемого объекта), так как все методы работы с массивами стандартны, мы можем смело их заимствовать и использовать на этих самых массивоподобных объектах.
Массивоподобный или итерируемый (array-like) объект — это объект ключи которого определены как неотрицательные целые числа. Они особенно хороши для добавления свойства length объекту. Почему? Потому что этого свойства не существует в объектах, но оно есть у массивов.
Тут нужно подчеркнуть, что в следующих примерах, когда мы вызываем Array.prototype , мы подтягиваем массив объекта и его прототип, в котором все его методы наследуются. И получается, что именно отсюда мы заимствуем методы массивов. Следовательно, использование кода, такого как Array.prototype.slice приведет к вызову метода slice, который определён в прототипе Array.
Давайте создадим массивоподобный объект и позаимствуем некоторые методы у массивов, чтобы применить их на нём же. Помните, что такой объект (array-like) это реальный объект и в целом это не массив:
Теперь, если вам надо применить какие-либо из распространенных методов массивов на нашем объекте, мы можем сделать следующее:
Тадам! Мы получаем все прелести работы над объектами и при этом нам доступны методы массивов, когда мы делаем наш объект массивоподобным и заимствуем дефолтные возможности работы с опять-таки массивами.
Объект arguments который является свойством всех функций в JavaScript — является массивоподобным объектом и по этой причине, одним из самых популярных применений call() и apply() методов это извлечение параметров переданных функции из этого объекта.
Переменная args это реальный массив. В нём содержатся все параметры, переданные функции transitionTo .
С этого примера мы узнали быстрый способ получения всех аргументов, переданных функции. А именно:
Мы обсудим то, как использовать метод apply() c arguments массивоподобным объектом немного позже, но уже на примере вариативных функций.
Заимствование строчных методов с помощью Apply() и Call()
Как и в предыдущем примере, мы также можем использовать apply() и call() , чтобы заимствовать строчные методы. Так как строки неизменяемые (иммутабельны), то только неманипулятивные массивы работают с ними, так что вы не сможете использовать reverse, pop и т.п.
Заимствование чужих методов и функций
Так как мы заимствуем, то давайте пойдем ва-банк и позаимствуем наши собственные кастомные методы и функции, а не только из Array (массивов) или из String (строк):
Будьте уверены, это не сколько просто, сколько рекомендуется — заимствовать наши собственные кастомные функции и методы. Объект gameController заимствует метод appController под именем avg() . Значение this, определенное в методе avg() будет выставлено первым параметром — то есть объектом gameController .
Вы возможно хотите знать, что случится если метод, который мы заимствуем изменится. Изменится ли сам заимствованный (скопированный) метод или этот метод останется полной копией оригинала без любой взаимосвязи с ним? Давайте ответим на эти вопросы быстрым и показательным примером:
Как и ожидалось, если мы изменим оригинальный метод, то изменения коснуться заимствованных примеров этого метода. Это ожидаемо по причине того, что мы никогда не делаем полную копию метода, мы просто её заимствуем (Ссылаясь на её актуальную реализацию).
Используем apply() для выполнения функций с составными переменными
Чтобы окончательно закончить наш разговор на тему многогранности и полезности методов Apply() , Call() и Bind() , мы обсудим одну очень интересную возможность, которую нам даёт метод Apply() . А именно, выполнение функции с массивом аргументов.
Мы можем передать массив с аргументами функции и воспользовавшись преимуществом метода apply() , функция будет работать с элементами массива так, как если бы мы запустили её таким образом:
Эта техника, в частности, используется для создания функций из составных переменных или как ещё известно — вариативных функций.
Это функции, которые принимают любое число аргументов, а не фиксированное. Арность функции указывает число аргументов, которое функции было определено принять.
Math.max() метод это общеизвестный пример вариативной функции в JavaScript:
Но, что если у нас массив из чисел, которые нам нужно передать к Math.max ? Вот так мы сделать не сможем:
Вот тут, с вариативными функциями, нам и поможет метод apply(). Вместо того, что мы увидели сверху, мы передадим массив чисел используя apply(), следовательно, код будет таким:
Как мы уже раньше узнали, первый аргумент в apply() выставляет значение this , но this не используется в Math.max() методе, поэтому мы передаём null .
Вот пример вариативной фукнции, чтобы ещё лучше проиллюстрировать концепцию использования метода apply() :
Заключение
Call() , Apply() и Bind() методы определенно рабочие лошадки и просто обязаны быть частью вашего JavaScript инструментария, особенно в плане выставления значения this у функций, для создания и выполнения вариативных функций и конечно же для заимствования методов и функций. Как JavaScript разработчик, вы с большой долей вероятности будете полагаться и использовать эти функции, снова и снова. Так что убедитесь в том, что вы хорошо их поняли.
Как вызвать функцию в функции javascript
Замыкания — это функции, ссылающиеся на независимые (свободные) переменные. Другими словами, функция, определённая в замыкании, «запоминает» окружение, в котором она была создана.
Независимые переменные — это все переменные, которые не были переданы как параметры и не были объявлены как локальные.
Для понимания замыкания рассмотрим фазы жизненного цикла функции
Создание функции
Создание функции состоит из описания — сигнатура + блок, а также записи свойств this + [[Scope]]
Свойство this указывает на объект, в котром находится функция изучим позднее
Свойство [[Scope]] недоступно для обращения в JS записывается окружение функции вне самой функции (т.е. ИМЕНА независимых переменных)
Присвоение функции переменной
При присвоении функции переменной (т.е. присвоение имени без вызова функции) происходит передача ссылки на объект функции.
Другимим словами — при присвоении функции переменной этой самой переменной передается и свойстов [[Scope]] со ссылкой на окружение функции на момент создания. Т.е. присвоение по ссылке — это не копирование.
Вызов функции
При вызове функции происходит создание и заполнение arguments и LexicalEnvironment или лексического окружения это свойство недоступно для обращения в JS. .
Лексическое окружение — это все доступные ЗНАЧЕНИЯ имен независимых переменных.
В LexicalEnvironment при вызове также записвается и [[Scope]] с окружением на момент создания.
Другими словами — функция помнит ИМЕНА окружения на момент своего создания и заполняет их ЗНАЧЕНИЯМИ на момент вызова.
Последовательность поиска — поиск значений осуществляется поиском имен внутри функции, а затем вне функции — последовательно поднимаясь из области видимости каждой вложенности до глобального объекта поиска.
Область видимости Подъем видимости
После выполнения функции все ее LexicalEnvironment (значения независимых переменных и аргументы) удаляются
Создаем замыкание
Каррироваие — это просто
Каррирование — это способ сделать из вызова foo(a,b,c) вызов foo(a)(b)(c)
Сделать это очень просто — нужно сделать внутри функции return другой функции с одним из параметров. А затем можно повторить return с новым параметром вунтри вложенной функции. Т.е. сколько параметров — столько и возвратов вложенных функций
Частичное применение — это спостоб присвоить одному из параметров постоянное значение для новой функции
Частичное применение Проверим видимость переменных
Замыкание
Стрелочная функция / Arrow Function
Все, отмеченное фоном у обычной функции заменяется на => у стрелочной функции
Без фигурных скобок и return
Со скобками и return
При наличии скобок обязательно нужно ставить return, иначе вернется undefined
Без параметров или fat синтаксис
Один аргумент или необязательніе () скобки
Три «нет» Arrow function
- Отсутствует arguments
- Не имеет собственного this this по месту создания
- Не рабтает с new и, как следствие, не рабоатет с super
Функция обратного вызова / Callback
Параметр и аргумент
Функция принимает данные, преобразовывает данные и возвращает данные.
В JavaScript вместо данных функции можно передать другую функцию как аргумент, ведь та в свою очередь все равно вместо себя подставит данные.
Функция как аргумент
Функция как параметр
Также JavaScript вместо данных можно указать на применение какой-то функции как параметр. А при вызове подставить конкретную функцию как аргумент. В итоге все равно будут передаваться вычисленные данные.
Функция как параметр
Функция, как значение массива
Этот пример показывает, что функция может быть значением массива и использоваться в вычислениях. Позже мы увидим, что массив это разновидность объекта, а функция внутри объекта называется метод.
Пример работы callback в браузере
Этот пример служит для демонстрации эксклюзивных возможностей callbak. Не все в этом примере изучено. Полное объяснение работы с DOM и событиями будет позже. Этот пример максимально прост и не рекомендутся к повторению.
Как передать функцию в функцию js

Функция в функцию передаётся как обычный параметр, так же, как это работает с любыми другими значениями. Например:
Частая ошибка, когда новички передают не саму функцию, а вызов функции. Будьте внимательны, при таком подходе, сначала вычислиться то, что мы передаём, затем это значение попадёт в функцию:
Ошибка возникает, потому что в функцию execFunc() передаётся не функция, а вызов функции sayHello() .