Функциональные выражения
Материал на этой странице устарел, поэтому скрыт из оглавления сайта.
Более новая информация по этой теме находится на странице https://learn.javascript.ru/function-expressions.
В JavaScript функция является значением, таким же как строка или число.
Как и любое значение, объявленную функцию можно вывести, вот так:
function sayHi() < alert( "Привет" ); >alert( sayHi ); // выведет код функции
Обратим внимание на то, что в последней строке после sayHi нет скобок. То есть, функция не вызывается, а просто выводится на экран.
Функцию можно скопировать в другую переменную:
function sayHi() < // (1) alert( "Привет" ); >var func = sayHi; // (2) func(); // Привет // (3) sayHi = null; sayHi(); // ошибка (4)
- Объявление (1) как бы говорит интерпретатору «создай функцию и помести её в переменную sayHi
- В строке (2) мы копируем функцию в новую переменную func . Ещё раз обратите внимание: после sayHi нет скобок. Если бы они были, то вызов var func = sayHi() записал бы в func результат работы sayHi() (кстати, чему он равен? правильно, undefined , ведь внутри sayHi нет return ).
- На момент (3) функцию можно вызывать и как sayHi() и как func()
- …Однако, в любой момент значение переменной можно поменять. При этом, если оно не функция, то вызов (4) выдаст ошибку.
Обычные значения, такие как числа или строки, представляют собой данные. А функцию можно воспринимать как действие.
Это действие можно запустить через скобки () , а можно и скопировать в другую переменную, как было продемонстрировано выше.
Объявление Function Expression
Существует альтернативный синтаксис для объявления функции, который ещё более наглядно показывает, что функция – это всего лишь разновидность значения переменной.
Он называется «Function Expression» (функциональное выражение) и выглядит так:
var f = function(параметры) < // тело функции >;
var sayHi = function(person) < alert( "Привет, " + person ); >; sayHi('Вася');
Сравнение с Function Declaration
«Классическое» объявление функции, о котором мы говорили до этого, вида function имя(параметры) <. >, называется в спецификации языка «Function Declaration».
- Function Declaration – функция, объявленная в основном потоке кода.
- Function Expression – объявление функции в контексте какого-либо выражения, например присваивания.
Несмотря на немного разный вид, по сути две эти записи делают одно и то же:
// Function Declaration function sum(a, b) < return a + b; >// Function Expression var sum = function(a, b)
Оба этих объявления говорят интерпретатору: «объяви переменную sum , создай функцию с указанными параметрами и кодом и сохрани её в sum «.
Основное отличие между ними: функции, объявленные как Function Declaration, создаются интерпретатором до выполнения кода.
Поэтому их можно вызвать до объявления, например:
sayHi("Вася"); // Привет, Вася function sayHi(name)
А если бы это было объявление Function Expression, то такой вызов бы не сработал:
sayHi("Вася"); // ошибка! var sayHi = function(name)
Это из-за того, что JavaScript перед запуском кода ищет в нём Function Declaration (их легко найти: они не являются частью выражений и начинаются со слова function ) и обрабатывает их.
А Function Expression создаются в процессе выполнения выражения, в котором созданы, в данном случае – функция будет создана при операции присваивания sayHi = function.
Как правило, возможность Function Declaration вызвать функцию до объявления – это удобно, так как даёт больше свободы в том, как организовать свой код.
Можно расположить функции внизу, а их вызов – сверху или наоборот.
Условное объявление функции
В некоторых случаях «дополнительное удобство» Function Declaration может сослужить плохую службу.
Например, попробуем, в зависимости от условия, объявить функцию sayHi по-разному:
var age = +prompt("Сколько вам лет?", 20); if (age >= 18) < function sayHi() < alert( 'Прошу вас!' ); >> else < function sayHi() < alert( 'До 18 нельзя' ); >> sayHi();
Function Declaration при use strict видны только внутри блока, в котором объявлены. Так как код в учебнике выполняется в режиме use strict , то будет ошибка.
А что, если использовать Function Expression?
var age = prompt('Сколько вам лет?'); var sayHi; if (age >= 18) < sayHi = function() < alert( 'Прошу Вас!' ); >> else < sayHi = function() < alert( 'До 18 нельзя' ); >> sayHi();
var age = prompt('Сколько вам лет?'); var sayHi = (age >= 18) ? function() < alert('Прошу Вас!'); >: function() < alert('До 18 нельзя'); >; sayHi();
Оба этих варианта работают правильно, поскольку, в зависимости от условия, создаётся именно та функция, которая нужна.
Анонимные функции
Взглянем ещё на один пример – функцию ask(question, yes, no) с тремя параметрами:
question Строка-вопрос yes Функция no Функция
Она выводит вопрос на подтверждение question и, в зависимости от согласия пользователя, вызывает функцию yes() или no() :
function ask(question, yes, no) < if (confirm(question)) yes() else no(); >function showOk() < alert( "Вы согласились." ); >function showCancel() < alert( "Вы отменили выполнение." ); >// использование ask("Вы согласны?", showOk, showCancel);
Какой-то очень простой код, не правда ли? Зачем, вообще, может понадобиться такая ask ?
…Оказывается, при работе со страницей такие функции как раз очень востребованы, только вот спрашивают они не простым confirm , а выводят более красивое окно с вопросом и могут интеллектуально обработать ввод посетителя. Но это всё потом, когда перейдём к работе с интерфейсом.
Здесь же обратим внимание на то, что то же самое можно написать более коротко:
function ask(question, yes, no) < if (confirm(question)) yes() else no(); >ask( "Вы согласны?", function() < alert("Вы согласились."); >, function() < alert("Вы отменили выполнение."); >);
Здесь функции объявлены прямо внутри вызова ask(. ) , даже без присвоения им имени.
Функциональное выражение, которое не записывается в переменную, называют анонимной функцией.
Действительно, зачем нам записывать функцию в переменную, если мы не собираемся вызывать её ещё раз? Можно просто объявить непосредственно там, где функция нужна.
Такого рода код возникает естественно, он соответствует «духу» JavaScript.
new Function
Существует ещё один способ создания функции, который используется очень редко, но упомянем и его для полноты картины.
Он позволяет создавать функцию полностью «на лету» из строки, вот так:
var sum = new Function('a,b', ' return a+b; '); var result = sum(1, 2); alert( result ); // 3
То есть, функция создаётся вызовом new Function(params, code) :
params Параметры функции через запятую в виде строки. code Код функции в виде строки.
Таким образом можно конструировать функцию, код которой неизвестен на момент написания программы, но строка с ним генерируется или подгружается динамически во время её выполнения.
Пример использования – динамическая компиляция шаблонов на JavaScript, мы встретимся с ней позже, при работе с интерфейсами.
Итого
Функции в JavaScript являются значениями. Их можно присваивать, передавать, создавать в любом месте кода.
- Если функция объявлена в основном потоке кода, то это Function Declaration.
- Если функция создана как часть выражения, то это Function Expression.
Между этими двумя основными способами создания функций есть следующие различия:
| Function Declaration | Function Expression | |
|---|---|---|
| Время создания | До выполнения первой строчки кода. | Когда управление достигает строки с функцией. |
| Можно вызвать до объявления | Да (т.к. создаётся заранее) | Нет |
| Условное объявление в if | Не работает | Работает |
Иногда в коде начинающих разработчиков можно увидеть много Function Expression. Почему-то, видимо, не очень понимая происходящее, функции решают создавать как var func = function() , но в большинстве случаев обычное объявление функции – лучше.
Если нет явной причины использовать Function Expression – предпочитайте Function Declaration.
Сравните по читаемости:
// Function Expression var f = function() < . >// Function Declaration function f()
Function Declaration короче и лучше читается. Дополнительный бонус – такие функции можно вызывать до того, как они объявлены.
Используйте Function Expression только там, где это действительно нужно и удобно.
Функции высшего порядка
Функция, принимающая в качестве аргументов другие функции или возвращающая другую функцию в качестве результата, называется функцией высшего порядка.
С функциями в JavaScript вы можете:
- Хранить их в качестве переменных.
- Использовать их в массивах.
- Назначать их в качестве свойств объекта (методов).
- Передавать их в качестве аргументов.
- Возвращать их из других функций.
Как и любой другой фрагмент данных. Вот самое главное, что вам нужно понять.
Функции работают с данными
Строки — это данные
sayHi = (name) => `Hi, $!`;
result = sayHi('User');console.log(result); // 'Hi, User!'
Числа — это данные
double = (x) => x * 2;
result = double(4);console.log(result); // 8
Булевой тип данных — это данные
getClearance = (allowed) => allowed ?
'Access granted' :
'Access denied';result1 = getClearance(true);
result2 = getClearance(false);console.log(result1); // 'Access granted'
console.log(result2); // 'Access denied'
Объекты — это данные
getFirstName = (obj) => obj.firstName;
result = getFirstName( firstName: 'Yazeed'
>);console.log(result); // 'Yazeed'
Массивы — это данные
len = (array) => array.length;
result = len([1, 2, 3]);console.log(result); // 3
Эти 5 типов данных являются объектами первого класса в любом из распространенных языков программирования.
Что делает их “первоклассными”? Вы можете передавать их, хранить их в переменных и массивах, использовать их в качестве исходных данных для вычислений. Вы можете использовать их, как и любой другой фрагмент данных.
Функции тоже могут быть данными
Функции в качестве аргументов
isEven = (num) => num % 2 === 0;
result = [1, 2, 3, 4].filter(isEven);console.log(result); // [2, 4]
Видите как filter использует isEven , чтобы решить, какие числа оставить? Функция isEven была аргументом другой функции.
Функция isEven вызывается функцией filter для каждого числа и использует возвращаемое значение true или false , чтобы определить, следует оставить число или выбросить.
Функции возвращающие функцию
add = (x) => (y) => x + y;
add запрашивает два аргумента, но по отдельности. Эта функция, запрашивающая только x , которая возвращает функцию, запрашивающую только y .
Опять же, это возможно только потому, что JavaScript позволяет функциям быть возвращаемым значением — таким же, как строки, числа, булевые значения и т.д.
Если захотите, вы по-прежнему сможете немедленно предоставить x и y двойным вызовом функции.
result = add(10)(20);console.log(result); // 30
Или же x сейчас, а y позже.
add10 = add(10);result = add10(20);console.log(result); // 30
Давайте вернемся к последнему примеру. add10 — это результат вызова функции add с одним аргументом. Попробуйте залогировать (logging) это в консоли.
add10 — это функция, которая берет y и возвращает x + y . После того, как вы предоставите y , она поспешит вычислить и вернуть ваш конечный результат.
Возможность повторного использования
Вероятно, самое большое преимущество функций высшего порядка — это возможность повторного использования. Без этих функций, методы массивов JavaScript — map , filter , и reduce не существовали бы!
Ниже представлен список пользователей. Мы собираемся сделать кое-какие вычисления с этой информацией.
users = [ name: 'Yazeed',
age: 25
>, name: 'Sam',
age: 30
>, name: 'Bill',
age: 20
>];
Map
Без функций высшего порядка, нам бы потребовались циклы для имитации функциональных возможностей метода map .
getName = (user) => user.name;
usernames = [];for (let i = 0; i < users.length; i++) const name = getName(users[i]);usernames.push(name);
>console.log(usernames);
// ["Yazeed", "Sam", "Bill"]
Но мы может сделать так:
usernames = users.map(getName);console.log(usernames);
// ["Yazeed", "Sam", "Bill"]
Filter
В мире без функций высшего порядка, нам бы все еще потребовались циклы, чтобы сымитировать функциональные возможности метода filter .
startsWithB = (string) => string
.toLowerCase()
.startsWith('b');namesStartingWithB = [];for (let i = 0; i < users.length; i++) if (startsWithB(users[i].name)) namesStartingWithB.push(users[i]);
>
>console.log(namesStartingWithB);
// [< "name": "Bill", "age": 20 >]
Но мы может сделать так:
namesStartingWithB = users
.filter((user) => startsWithB(user.name));console.log(namesStartingWithB);
// [< "name": "Bill", "age": 20 >]
Reduce
Да, это касается и метода reduce . Нельзя сделать столько крутых вещей без функций высшего порядка!
total = 0;for (let i = 0; i < users.length; i++) total += users[i].age;
>console.log(total);
// 75
Но как насчет этого?
totalAge = users
.reduce((total, user) => user.age + total, 0);console.log(totalAge);
// 75
Заключение
- Строки, числа, булевые значения, массивы и объекты могут храниться в качестве переменных, массивов, аргументов или методов.
- JavaScript обрабатывает все функции одинаково.
- Map, filter и reduce значительно упрощают преобразование, поиск и суммирование списков.
Передача функции в функцию — Первоклассные значения в JavaScript
В информатике существует термин «Первоклассные значения» . Javascript это тот язык, в котором это понятие применимо к функциям .
Но обо всем по порядку. Сначала нужно ответить на вопрос: что такое первоклассные значения в программировании?
Для первоклассных значений характерно следующее :
- Их можно присваивать переменным или сохранять/заносить в массив или объект.
- Передавать эти значения функциям в качестве параметра.
- Возвращать из функций.
В Javascript все перечисленные действия можно выполнять с функциями . То есть функцию можно присвоить переменной, передать в другую функцию в качестве параметра или вернуть из функции.
Здесь уже шла речь о том, что функции можно рассматривать как значения . А также о том, как происходит присваивание функции переменной.
Поэтому далее поговорим о том, как происходит передача функции в качестве параметра в другую функцию .
Первоклассные значения в javascript?
- undefined — неопределенное значение
- null — несуществующий объект
- 0 — ноль
- «empty string» — пустая строка
- NaN — не число
- object — объект
- array — массив
- number — число
- string — строка
Напомним, что псевдоложные значения при составлении условий возвращают ложь false, а псевдоистинные, соответственно, истину true.
Передача функции в функцию
Для примера того, как в Javascript происходит передача функции другой функции, возьмем массив, состоящий из объектов.
Каждый объект массива состоит из двух свойств: value и returned. Первое содержит название одного из псевдоложных значений, значением второго является ложь — false:
То есть исходный массив — это своего рода структура данных, которая содержит перечень всех псевдоложных значений и указание на то, что все они при составлении условий возвращают ложь false.
Сделаем в нашей структуре некоторые изменения : Во-первых, укажем, что одно из псевдоложных значений возвращает истину true, что является ошибкой и, во-вторых, вместо одного из псевдоложных значений укажем псевдоистиное, что также не допустимо , так как предполагается, что исходный массив должен содержать исключительно псевдоложные значения .
Итак, в массиве из объектов сделано две ошибки:
1. В нем присутствует псевдоистиное значение string .
2. Указано, что псевдоложное значение null возвращает истину true.
Используя передачу функции в качестве параметра другой функции , проверим массив и выявим ошибки. Для этого :
Составим одну основную функцию , которая будет перебирать все значения массива, то есть каждый из объектов.
function analysis_each ( array , analysis_function ) for( i = 0 ; i < array .length; i ++)
if( analysis_function ( array [ i ])) < /* if true */
return false
- Функция analysis_each анализирует каждый элемент массива array на предмет его истинности (будем анализировать массив pseudo_false) .
- Функция возвращает false, если условие истинно и на этом заканчивает свою работу. Если же условие ложно, то цикл повторяется.
- Если ошибок в массиве не найдено, то функция возвращает true.
Теперь составим вспомогательные функции, которые мы будем передавать в качестве параметра в основную функцию.
С помощью 1-ой в условии функции analysis_each будут рассматриваться значения свойства returned каждого объекта массива. На данном этапе задача состоит в том, чтобы выяснить, есть ли среди них true.
function analysis_returned ( array ) return ( array . returned );
>
2-я функция предназначена для сравнения значения свойства value каждого объекта массива с псевдоистиным значением string . Результат этого сравнения также будет фигурировать в условии основной функции.
function analysis_value ( array ) return ( array . value == «string» );
>
Теперь мы можем приступить к тому, ради чего написана эта статья: к передаче функции в качестве параметра в другую функцию .
Итак, передаем поочередно каждую из вспомогательных функций analysis_returned и analysis_value в основную функцию analysis_each.
При этом каждый раз функцию analysis_each будем присваивать переменной.
var search_true = analysis_each ( pseudo_false , analysis_returned );
if(!search_true)
console . log ( ‘Ошибка, одному из псевдоложных значений присвоена ИСТИНА’) ;
>
var search_string = analysis_each ( pseudo_false , analysis_value );
if( !search_string )
console . log ( ‘Внимание, в списке псевдоложных значений присутствует псевдоИСТИНОЕ значение «string»‘ );
>
- Функция analysis_returned будучи переданной в качестве параметра в analysis_each позволяет проверить значение свойства returned каждого объекта массива pseudo_false.
- Функция analysis_value при передачи в функцию analysis_each позволяет проверить свойство value каждого объекта массива pseudo_false.
- Одновременно с этим мы дважды присваиваем функцию analysis_each различным переменным.
Далее приведен весь код этого примера.
Фрагмент кода
/* Основная функция analysis_each перебирает все элементы массива (все объекты), каждый из которых является параметром функции analysis_function */
function analysis_each(array, analysis_function) for(i = 0; i < array.length; i++)if(analysis_function(array[i])) /* if true */
return false
>
>
return true
>
/* Функция для анализа свойства returned */
function analysis_returned(array) return (array.returned);
>
/* Функция для анализа свойства value */
function analysis_value(array) return (array.value == ‘string’);
>
/* Присваивание функций переменным */
var search_true = analysis_each(pseudo_false, analysis_returned);
if(!search_true) console.log( ‘Ошибка, одному из псевдо ЛОЖНЫХ значений присвоена ИСТИНА’ );
>
else console.log( ‘No Error’ );
>
var search_string = analysis_each(pseudo_false, analysis_value);
if(!search_string) console.log( ‘Внимание, в списке псевдоложных значений присутствует псевдоИСТИНОЕ значение «string»‘ );
>
else console.log( ‘No Error’ );
>
Ошибка, одному из псевдо ЛОЖНЫХ значений присвоена ИСТИНА
Внимание, в списке псевдоложных значений присутствует псевдоИСТИНОЕ значение «string»
Передача функции в функцию — Тонкости и нюансы
Теперь составим массив из псевдоиcтинных значений.
Сделаем в нем ошибку:
Пробуем проверить. Ищем в массиве false.
При этом функция analysis_returned не сможет нам в этом помочь. Почему?
Потому, что значения свойств returned в массиве pseudo_true в основном равны true. Как вы помните, если в условии функции analysis_each будет истина, то она выдаст нам false, а значит ошибку, в то время как в данном случае ошибки нет.
Выходит, что мы увидим ошибку в любом случае: есть она или нет.
Значит, для проверки массива pseudo_true нужна отдельная вспомогательная функция. Назовем ее analysis_returned_true.
function analysis_returned_true ( array ) return ( ! array . returned );
>
Как видите функция analysis_returned_true идентична функции analysis_returned, но с одной существенной разницей : благодаря восклицательному знаку !array.returned мы будем получать значение свойства returned противоположное реально существующему.
Так как же теперь будет работать функция analysis_each, если в нее в качестве параметра передать функцию analysis_returned_true?
Напомним, как выглядит функция analysis_each :
function analysis_each ( array , analysis_function ) for( i = 0 ; i < array .length; i ++)
if( analysis_function ( array [ i ])) < /* if true */
return false
- Теперь значения true свойства returned массива pseudo_true будут фигурировать в условии функции analysis_each как ложные, и цикл будет повторяться.
- Если же свойство returned будет равно false, то в условии функции analysis_each окажется истина true и проверка будет остановлена. Таким образом, ошибка будет найдена.
Далее приведен весь код, где проводится работа с массивом из псевдоистинных значений.
Фрагмент кода
/* Основная функция analysis_each перебирает все элементы массива (все объекты), каждый из которых является параметром функции analysis_function */
function analysis_each(array, analysis_function) for(i = 0; i < array.length; i++)if(analysis_function(array[i])) < /* if true */
return false
>
>
return true
>
/* Функция для анализа свойства returned */
function analysis_returned_true(array) return ( ! array.returned);
>
/* Присваивание функций переменной */
var search_false = analysis_each(pseudo_true, analysis_returned_true);
if(!search_false) console.log( ‘Ошибка, одному из псевдо ИСТИННЫХ значений присвоена ЛОЖЬ’ );
>
else console.log( ‘No Error’ );
>
Ошибка, одному из псевдо ИСТИННЫХ значений присвоена ЛОЖЬ
В зависимости от способностей, тема передачи функции другой функции может оказаться довольно сложной для понимания. Но постарайтесь вникнуть.
Читайте также.

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

Передача объекта в Функцию — Переменная ссылается на объект

Функция isNaN — Число или не число

Функции parseInt, parseFloat — Конвертация строк

Что нужно для изучения javascript?

Метод write() объекта document
В чем смысл земной жизни? +ВИДЕО
В воскресенье, 10 сентября, в магазине «Сретение» состоялась традиционная беседа с прихожанами. В этот раз иеромонах Лавр (Архипов) обсудил со слушателями вопрос, который волновал и волнует каждого: в чем смысл жизни?
Запись встречи (Длительность – 00:45:25)
В самом начале встречи священник отметил, что смысл бытия у человечества должен быть один. Чтобы его определить, отец Лавр предложил слушателям вспомнить главы из книги Бытия о сотворении человека, ведь в процессе творения заложен и смысл дальнейшего существования. Библия говорит нам, что Бог создал человека по Своему образу и подобию. Значит, смысл жизни заключается в том, чтобы уподобиться Небесному Отцу посредством данной нам свободой воли и при Божией помощи. Если же человек будет употреблять данную ему свободу для деградации, он никогда не исполнит своего великого предназначения.

Что же мешает нам находиться в постоянном возрастании в Господе? Почему процесс изменения себя связан с болью и искушениями? Неужели Бог сотворил зло, чтобы мы страдали? По мысли отца Лавра, зло – это извращенная воля человека или ангела, которая направлена не ко Творцу. Более всего это искажение проявляется в гордыне, которая погубила как Денницу, так и Адама с Евой, которая неизменно отравляет жизнь и нам, когда мы ставим на первое место не Бога, а самих себя. В подтверждение своих слов священник привел цитаты из книги игумена Никона (Воробьева) «Внимай себе. Письма о духовной жизни». В ней собраны духовные наставления известного старца о путях к Богу и борьбе с различными страстями.

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