Js передать функцию как параметр
Перейти к содержимому

Js передать функцию как параметр

  • автор:

 

Pass a JavaScript function as parameter

It works, but the problem is that refreshContactList fires when the function is called, rather than when it’s used in the function.

I could get around it using eval() , but it’s not the best practice, according to what I’ve read. How can I pass a function as a parameter in JavaScript?

15 Answers 15

You just need to remove the parenthesis:

This then passes the function without executing it first.

Here is an example:

Fenton's user avatar

If you want to pass a function, just reference it by name without the parentheses:

But sometimes you might want to pass a function with arguments included, but not have it called until the callback is invoked. To do this, when calling it, just wrap it in an anonymous function, like this:

If you prefer, you could also use the apply function and have a third parameter that is an array of the arguments, like such:

dallin's user avatar

To pass the function as parameter, simply remove the brackets!

The idea behind this is that a function is quite similar to a variable. Instead of writing

you might as well write

There are minor differences between the two, but anyway — both of them are valid ways to define a function. Now, if you define a function and explicitly assign it to a variable, it seems quite logical, that you can pass it as parameter to another function, and you don’t need brackets:

Js передать функцию как параметр

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

Например, определим простейшую функцию, которая принимает один параметр:

Функция print() принимает один параметр — message. Поэтому при вызове функции мы можем передать для него значение, например, некоторую строку:

Передаваемые параметрам значения еще называют аргументами.

При этом в отличие от ряда других языков программирования мы в принципе можем не передавать значения параметрам. Например:

Если параметру не передается значение, тогда он будет иметь значение undefined .

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

При вызове функции с несколькими параметрами значения передаются параметрам по позиции. То есть первое значение передается первому параметру, второе значение — второму и так далее. Например, в вызове:

Число 2 передается параметру a , а число 6 — параметру b .

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

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

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

Также мы можем напрямую определять для параметров значения по умолчанию:

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

При этом значение параметра по умолчанию может быть производным, представлять выражение:

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

Массив arguments

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

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

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

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

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

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

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

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

Передача функций параметрами в JavaScript

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

Рассмотрим подробнее на практическом примере. Пусть у нас есть функция test , принимающая два параметра:

Давайте в первый параметр функции test передадим анонимную функцию, возвращающую 1 , а во второй параметр — анонимную функцию, возвращающую 2 :

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

При определении функции мы указали два параметра — func1 и func2 . Эти параметры ничего не знают про то, что в них будет передаваться. Можем, например, передать числа:

А можем передать функции:

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

Давайте выведем в консоль сумму результатов первой и второй функции:

Сделайте функцию test , параметрами принимающую 3 функции. Передайте в нее первым параметром функцию, возвращающую 1 , вторым — функцию, возвращающую 2 , третьим — функцию, возвращающую 3 . Выведите в консоль сумму результатов функций.

Именованные функции

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

Давайте сделаем их как Function Declaration. Первую функцию назовем get1 , а вторую — get2 :

Передадим в параметры функции test имена функций get1 и get2 (то есть их исходный код, а не результат):

Переделаем на Function Expression:

Сделайте функцию test , параметрами принимающую 3 функции и возвращающую сумму результатов переданных функций.

Сделайте 3 функции, объявив их как Function Declaration и дав им имена func1 , func2 и func3 . Пусть первая функция возвращает 1 , вторая — 2 , а третья — 3 . Передайте эти функции параметром в функцию test из предыдущей задачи.

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

Параметры передаваемых функций

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

Пусть переданная функция func параметром принимает число и что-то с ним делает. Передадим ей, например, число 3 :

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

В результате всего этого наша конструкция выведет квадрат числа 3 , то есть 9 :

Оформим код более изящно:

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

Переделайте ваш код так, чтобы передаваемая функция была не анонимной, а была определена как Function Declaration с именем func .

Переделайте передаваемую функцию на Function Expression с тем же именем func .

Пусть передаваемая функция теперь принимает два параметра и возвращает их сумму. При вызове передаваемой функции внутри test передайте в передаваемую функцию число 2 и число 3 . Выведите алертом результат.

Передадим и число параметром

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

Воспользуемся нашей функцией:

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

Мы можем, к примеру, вторым параметром функции test передать функцию, возводящую в квадрат, а можем, к примеру, возводящую в куб:

Пусть функция test первым параметром принимает число, а вторым и третьим параметрами — функции, также параметром принимающие числа.

Пусть функция test возвращает сумму результатов переданных функций:

Вызовите функцию test , первым параметром передав число 3 , вторым параметром функцию, возводящую число в квадрат, а третьим — функцию, возводящую число в куб. Выведите результат работы в консоль.

Применение

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

Применим нашу функцию к какому-нибудь массиву:

Оформим вызов нашей функции изящнее (так более принято):

Не подсматривая в мой код реализуйте такую же функцию test самостоятельно.

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

Функции

Функции — ключевая концепция в JavaScript. Важнейшей особенностью языка является поддержка функции первого класса (functions as first-class citizen). Любая функция это объект, и следовательно ею можно манипулировать как объектом, в частности:

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

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

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

Для понимания работы функций необходимо (и достаточно?) иметь представление о следующих моментах:

 

  • способы объявления
  • способы вызова
  • параметры и аргументы вызова ( arguments )
  • область данных ( Scope ) и замыкания ( Closures )
  • объект привязки ( this )
  • возвращаемое значение ( return )
  • исключения ( throw )
  • использование в качестве конструктора объектов
  • сборщик мусора ( garbage collector )

Объявление функций

Функции вида «function declaration statement»

Объявление функции (function definition, или function declaration, или function statement) состоит из ключевого слова function и следующих частей:

  • Имя функции.
  • Список параметров (принимаемых функцией) заключённых в круглые скобки () и разделённых запятыми.
  • Инструкции, которые будут выполнены после вызова функции, заключают в фигурные скобки < >.

Например, следующий код объявляет простую функцию с именем square:

Функция square принимает один параметр, названный number. Состоит из одной инструкции, которая означает вернуть параметр этой функции (это number ) умноженный на самого себя. Инструкция return указывает на значение, которые будет возвращено функцией.

Примитивные параметры (например, число) передаются функции значением; значение передаётся в функцию, но если функция меняет значение параметра, это изменение не отразится глобально или после вызова функции.

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

Функции вида «function definition expression»

Функция вида «function declaration statement» по синтаксису является инструкцией (statement), ещё функция может быть вида «function definition expression». Такая функция может быть анонимной (она не имеет имени). Например, функция square может быть вызвана так:

Однако, имя может быть и присвоено для вызова самой себя внутри самой функции и для отладчика (debugger) для идентифицированные функции в стек-треках (stack traces; «trace» — «след» / «отпечаток»).

Функции вида «function definition expression» удобны, когда функция передаётся аргументом другой функции. Следующий пример показывает функцию map , которая должна получить функцию первым аргументом и массив вторым.

В следующем коде наша функция принимает функцию, которая является function definition expression, и выполняет его для каждого элемента принятого массива вторым аргументом.

Функция возвращает: [0, 1, 8, 125, 1000].

В JavaScript функция может быть объявлена с условием. Например, следующая функция будет присвоена переменной myFunc только, если num равно 0:

В дополнение к объявлениям функций, описанных здесь, вы также можете использовать конструктор Function для создания функций из строки во время выполнения (runtime), подобно eval() .

Метод — это функция, которая является свойством объекта. Узнать больше про объекты и методы можно по ссылке: Работа с объектами.

Вызовы функций

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

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

Эта инструкция вызывает функцию с аргументом 5. Функция вызывает свои инструкции и возвращает значение 25.

Функции могут быть в области видимости, когда они уже определены, но функции вида «function declaration statement» могут быть подняты (поднятиеhoisting), также как в этом примере:

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

Примечание: Это работает только тогда, когда объявлении функции использует вышеупомянутый синтаксис (т.е. function funcName()<> ). Код ниже не будет работать. Имеется в виду то, что поднятие функции работает только с function declaration и не работает с function expression.

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

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

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

Есть другие способы вызвать функцию. Существуют частые случаи, когда функции необходимо вызывать динамически, или поменять номера аргументов функции, или необходимо вызвать функцию с привязкой к определённому контексту. Оказывается, что функции сами по себе являются объектами, и эти объекты в свою очередь имеют методы (посмотрите объект Function ). Один из них это метод apply() , использование которого может достигнуть этой цели.

Область видимости функций

(function scope)

Переменные объявленные в функции не могут быть доступными где-нибудь вне этой функции, поэтому переменные (которые нужны именно для функции) объявляют только в scope функции. При этом функция имеет доступ ко всем переменным и функциям, объявленным внутри её scope. Другими словами функция объявленная в глобальном scope имеет доступ ко всем переменным в глобальном scope. Функция объявленная внутри другой функции ещё имеет доступ и ко всем переменным её родительской функции и другим переменным, к которым эта родительская функция имеет доступ.

Scope и стек функции

(function stack)

Рекурсия

Функция может вызывать саму себя. Три способа такого вызова:

  1. по имени функции
  2. по переменной, которая ссылается на функцию

Для примера рассмотрим следующие функцию:

Внутри функции (function body) все следующие вызовы эквивалентны:

  1. bar()
  2. arguments.callee()
  3. foo()

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

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

Однако некоторые алгоритмы не могут быть простыми повторяющимися циклами. Например, получение всех элементов структуры дерева (например, DOM (en-US) ) проще всего реализуется использованием рекурсии:

В сравнении с функцией loop , каждый рекурсивный вызов сам вызывает много рекурсивных вызовов.

Также возможно превращение некоторых рекурсивных алгоритмов в нерекурсивные, но часто их логика очень сложна, и для этого потребуется использование стека (stack). По факту рекурсия использует stack: function stack.

Поведение стека можно увидеть в следующем примере:

Вложенные функции (nested functions) и замыкания (closures)

Вы можете вложить одну функцию в другую. Вложенная функция (nested function; inner) приватная (private) и она помещена в другую функцию (outer). Так образуется замыкание (closure). Closure — это выражение (обычно функция), которое может иметь свободные переменные вместе со средой, которая связывает эти переменные (что «закрывает» («close») выражение).

Поскольку вложенная функция это closure, это означает, что вложенная функция может «унаследовать» (inherit) аргументы и переменные функции, в которую та вложена. Другими словами, вложенная функция содержит scope внешней («outer») функции.

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

Следующий пример показывает вложенную функцию:

Поскольку вложенная функция формирует closure, вы можете вызвать внешнюю функцию и указать аргументы для обоих функций (для outer и innner).

Сохранение переменных

Обратите внимание, значение x сохранилось, когда возвращалось inside . Closure должно сохранять аргументы и переменные во всем scope. Поскольку каждый вызов предоставляет потенциально разные аргументы, создаётся новый closure для каждого вызова во вне. Память может быть очищена только тогда, когда inside уже возвратился и больше не доступен.

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

Несколько уровней вложенности функций (Multiply-nested functions)

Функции можно вкладывать несколько раз, т.е. функция (A) хранит в себе функцию (B), которая хранит в себе функцию (C). Обе функции B и C формируют closures, так B имеет доступ к переменным и аргументам A, и C имеет такой же доступ к B. В добавок, поскольку C имеет такой доступ к B, который имеет такой же доступ к A, C ещё имеет такой же доступ к A. Таким образом closures может хранить в себе несколько scope; они рекурсивно хранят scope функций, содержащих его. Это называется chaining (chain — цепь; Почему названо «chaining» будет объяснено позже)

Рассмотрим следующий пример:

В этом примере C имеет доступ к y функции B и к x функции A . Так получается, потому что:

  1. Функция B формирует closure, включающее A , т.е. B имеет доступ к аргументам и переменным функции A .
  2. Функция C формирует closure, включающее B .
  3. Раз closure функции B включает A , то closure С тоже включает A, C имеет доступ к аргументам и переменным обоих функций B и A . Другими словами, С связывает цепью (chain) scopes функций B и A в таком порядке.

В обратном порядке, однако, это не верно. A не имеет доступ к переменным и аргументам C , потому что A не имеет такой доступ к B . Таким образом, C остаётся приватным только для B .

Конфликты имён (Name conflicts)

Когда два аргумента или переменных в scope у closure имеют одинаковые имена, происходит конфликт имени (name conflict). Более вложенный (more inner) scope имеет приоритет, так самый вложенный scope имеет наивысший приоритет, и наоборот. Это цепочка областей видимости (scope chain). Самым первым звеном является самый глубокий scope, и наоборот. Рассмотрим следующие:

Конфликт имени произошёл в инструкции return x * 2 между параметром x функции inside и переменной x функции outside . Scope chain здесь будет таким: < inside ==> outside ==> глобальный объект (global object)>. Следовательно x функции inside имеет больший приоритет по сравнению с outside , и нам вернулось 20 (= 10 * 2), а не 10 (= 5 * 2).

Замыкания

(Closures)

Closures это один из главных особенностей JavaScript. JavaScript разрешает вложенность функций и предоставляет вложенной функции полный доступ ко всем переменным и функциям, объявленным внутри внешней функции (и другим переменным и функции, к которым имеет доступ эта внешняя функция).

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

Также, поскольку вложенная функция имеет доступ к scope внешней функции, переменные и функции, объявленные во внешней функции, будет продолжать существовать и после её выполнения для вложенной функции, если на них и на неё сохранился доступ (имеется ввиду, что переменные, объявленные во внешней функции, сохраняются, только если внутренняя функция обращается к ним).

Closure создаётся, когда вложенная функция как-то стала доступной в неком scope вне внешней функции.

Более сложный пример представлен ниже. Объект с методами для манипуляции вложенной функции внешней функцией можно вернуть (return).

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

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

Использование объекта arguments

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

где i — это порядковый номер аргумента, отсчитывающийся с 0. К первому аргументу, переданному функции, обращаются так arguments[0] . А получить количество всех аргументов — arguments.length .

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

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

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

Примечание: arguments является псевдо-массивом, но не массивом. Это псевдо-массив, в котором есть пронумерованные индексы и свойство length . Однако он не обладает всеми методами массивов.

Рассмотрите объект Function в JavaScript-справочнике для большей информации.

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

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

Параметры по умолчанию (Default parameters)

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

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

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

Для более детального рассмотрения ознакомьтесь с параметрами по умолчанию.

Остаточные параметры (Rest parameters)

Остаточные параметры предоставляют нам массив неопределённых аргументов. В примере мы используем остаточные параметры, чтобы собрать аргументы с индексами со 2-го до последнего. Затем мы умножим каждый из них на значение первого аргумента. В этом примере используется стрелочная функция (Arrow functions), о которой будет рассказано в следующей секции.

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

(Arrow functions)

Стрелочные функции — функции вида «arrow function expression» (неверно fat arrow function) — имеют укороченный синтаксис по сравнению с function expression и лексически связывает значение this . Стрелочные функции всегда анонимны. Посмотрите также пост блога hacks.mozilla.org «ES6 In Depth: Arrow functions».

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

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

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

Лексика this

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

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

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

В arrow function значением this является окружающий его контекст, так следующий код работает ожидаемо:

Далее

Подробное техническое описание функций в статье справочника Функции

Смотрите также Function (en-US) в Справочнике JavaScript для получения дополнительной информации по функции как объекту.

 

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *