Как настроить смартфоны и ПК. Информационный портал

JavaScript: Функции. Функциональное программирование

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

Объявление и вызов функции JavaScript

Синтаксис функции JavaScript выглядит следующим образом:

function ""имя"" (""аргумент1"", ""аргумент2"", ""аргумент3"" ...) { ""операторы"" return ""значение"" }

Имя определяет, как мы будем называть функцию при ее вызове. Аргументы задают значения, которые передаются функции для обработки. Раздел операторы представляет собой тело функции, которая выполняет обработку. Необязательный оператор return позволяет вернуть значение.

В следующем примере показана функция, определяемая в разделе HTML-страницы и вызываемая в разделе :

function sayHello() { alert("Привет!"); } sayHello();

Передача аргументов в функцию

В приведенном выше примере (script type text JavaScript function ) функции не передается никакие аргументы. Обычно функция предназначена для выполнения каких-либо действий с несколькими аргументами:

Простой пример функции JavaScript function sayHello(day, month) { alert("Привет! Сегодня " + day + " " + month); } sayHello("24", "Июля"); sayHello ("1", "Августа"); sayHello ("24", "Мая");

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

Возврат значения из функции

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

Простой пример функции JavaScript var result = addValues(10, 20) document.write ("Результат = " + result);

В приведенном выше примере мы передаем в функцию addValues значения 10 и 20 . Функция addValues складывает эти два значения и возвращает результат. Оператор return присваивает результат переменной result, которая затем используется для создания строки, выводимой на HTML-странице .

Вызов JavaScript function может быть выполнен в разных местах. Например, не обязательно присваивать результат в качестве значения переменной. Можно использовать его непосредственно в качестве аргумента при вызове document.write .

Важно отметить, что функция может возвращать только одно значение:

Простой пример функции JavaScript function addValues(value1, value2) { return value1 + value2; } document.write ("Результат = " + addValues(10, 20)); JavaScript onclick function также могут использоваться в условных выражениях. Например: Простой пример функции JavaScript function addValues(value1, value2) { return value1 + value2; } if (addValues(10, 20) > 20) { document.write ("Результат > 20"); } else { document.write ("Результат < 20"); }

Где размещать объявления функций

Есть два места, в которых рекомендуется размещать объявления JavaScript function return: внутри раздела HTML-документа или во внешнем файле .js . Наиболее предпочтительным местом считается второй вариант, так как он обеспечивает наибольшую гибкость.

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

Перевод статьи «Understanding JavaScript Functions » был подготовлен дружной командой проекта .

Хорошо Плохо

Любому программисту хорошо известно, что такое функции и зачем они нужны. Однако функциям в языке Javascript присущи некоторые особенности. Если вы давно программируете именно на этом языке, то наверняка знаете, что существуют разные . Если же вы пришли из другого языка, то при прочтении некоторых статей скорее всего видели вот такое, странное на первый взгляд, объявление функции:

Var add = function(arg1, arg2) { var sum = arg1 + arg2; return sum; } var result = add(5, 2); //result теперь 7

То есть функция, во-первых, не имеет имени. Во-вторых, она присваивается переменной, но не просто присваивается, а тут же идет ее тело. Лично у меня, до этого писавшего на таких языках как VB, C++, подобное объявление вызывало недоумение и непонимание, как это устроено и зачем вообще так писать.

Я привык к "классическому" объявлению функции и ее вызову, вот так:

Function add(arg1, arg2) { var sum = arg1 + arg2; return sum; } var result = add(5, 3); //result теперь 8

И вот здесь как раз мы подходим к особенностям функций в Javascript. Для простоты понимания представьте себе, что функция в JS - это обычное значение, как, например, число или строка. Вы же можете записать в переменную result число 5? Или что-то более сложное, вроде массива, а потом вывести это на экран? Можете. Так вот, если представить, что функция - это обычное значение, пусть и весьма сложной структуры, то первый способ объявления уже не кажется чем-то невероятным.

Следующий интересный факт является логическим продолжением первого. После того как мы помещаем данные в переменную, мы можем посредством имени этой переменной передать данные в другую переменную:

Var a = 5; var b = a; alert(b); //выведет 5

Обычное дело. А теперь взгляните вот на этот код:

Var add = function(arg1, arg2) { var sum = arg1 + arg2; return sum; } var calcSum = add; alert(calcSum(5, 6)); //выведет 11

Начинаете уже догадываться? Поскольку функция - как переменная, мы можем "размножать" ее посредством обычного присваивания в другие переменные, превращая их так же в функции. Теперь calcSum тоже умеет складывать два числа. Однако код

Var calcSum = add(1, 1); //calcSum теперь равно 2, это не функция, а переменная с числом alert(calcSum(5, 6)); //ошибка

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

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

Var calcFact = function fact(val) { if (val == 1) ? val: val * fact(val — 1); //вычисление факториала с помощью рекурсии } alert(calcFact(4)); //выведет 24

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

Возможно, вы задаетесь вопросом "Хм, интересная возможность! Но какое преимущество у этого способа? Есть ли ситуации, когда без этого не обойтись или это хотя бы более удобно, чем обычное объявление?". Не возьмусь утверждать, что есть ситуации, где без такого подхода обойтись нельзя, но пример, где он уменьшает количество кода, привести могу. Допустим, вам нужно поприветствовать человека в зависимости от времени суток:

Var date = new Date(); var hello = (date.getHours() < 12) ? function() {alert("Доброе утро!")} : (date.getHours() < 18) ? function() {alert("Добрый день!")} : function() {alert("Добрый вечер!")}; hello();

Как видите, функции крайне простые, с одной-единственной командой alert.

Если бы мы решили пойти "классическим путем", то пришлось бы писать три отдельные функции, а потом вызывать их в условии проверки времени:

Function goodMorning() { alert("Доброе утро!"); } function goodAfternoon() { alert("Добрый день!"); } function goodEvning() { alert("Добрый вечер!"); } var date = new Date(); (date.getHours() < 12) ? goodMorning() : (date.getHours() < 18) ? goodAfternoon() : goodEvning();

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

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

Function add(a, b) { return a + b; } var calcSum = add; calcSum(5, 5);

Уже нельзя. Поэтому в нашем примере, если все-таки понадобится приветствовать гостя не единожды, нам придется каждый раз дублировать вот этот фрагмент:

(date.getHours() < 12) ? goodMorning() : (date.getHours() < 18) ? goodAfternoon() : goodEvning();

А в первом случае достаточно будет написать всего лишь hello(); и результат будет тот же.

Я рассказал вам про интересную особенность функций JS и привел примеры. Таким образом вы увидели, что способы вызова функций в Javascript не ограничиваются одним-единственным видом. Даже если вы и не сможете сразу найти применение этим возможностям в ваших проектах, то хотя бы будете знать, что такие возможности есть. И когда подвернется действительно удачный случай, то сможете сократить объем кода и избежать лишней путаницы и ошибок.!

Идеи динамичного формирования контента на web-ресуре стали нормой. Статические страницы и шаблонное сайтостроение окончательно завершили свою миссию.

Однако современный web-ресурс не обязательно должен быть представлен набором страниц, формируемых сервером и обновляемых браузером (JS+AJAX).

Web-ресурс в момент прихода посетителя может представлять собой пару заголовков для протокола, немного текста в «head», несколько строк кода в «body» и все. Остальное «додумается » в процессе работы посетителя - это идеальный сайт или стремящийся быть таковым.

Место описания и сущность функций

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

Понятие алгоритма вне функции здесь отсутствует в принципе. Разумеется, разработчик может в любом месте страницы вставить скрипт, поместить в него код и он будет исполнен. Но какой смысл в коде, который исполняется только раз: при загрузке (перегрузке) страницы? Разве что можно установить начальные значения каких-нибудь малозначимых переменных.

Скрипт - это место описания нужных переменных и функций, нежели добрый кусок кода, написанный ради самого себя. Именно набор функций является существенным и значимым, возможно - их взаимная непосредственная связь, но чаще бывает все иначе. Место описания функции и место ее применения вовсе не одно и тоже.

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

Функциональная динамика

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

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

Изначально здесь нет никакой последовательности и нет никакой параллельности. Здесь есть адекватная реакция web-ресурса на события. Насколько быстро JavaScript отработает ту или иную функцию, зависит от многих технических (компьютер, линии связи) и семантических (логика алгоритма, предметная область, смысл задачи) факторов.

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

Это новое мышление в разработке: распределенная обработка информации в недрах отдельно взятого браузера!

Синтаксис переменных и функций

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

Описание функции в общем случае начинается с ключевого слова «function», за которым следует ее имя, список аргументов в круглых скобках через запятую и тело функции в фигурных скобках.

В данном примере описаны две функции, обеспечивающие AJAX-обмен между страницей и сервером. Переменная scXHR описана выше, потому доступна как в InitXML, так и внутри WaitReplySC.

Имя функции и парамет «функция»

Здесь был представлен асинхронный вариант, когда JavaScript-функция в функции вызывается после ответа сервера. При этом, получив ответ от сервера, WaitReplySC обращается к тегам страницы, заполняет их полученной информацией и вызывает другие функции, которые вполне могут инициировать следующий запрос к серверу.

Здесь важно также отметить, что WaitReplySC - это функция. Но в строке scXHR.onreadystatechange = WaitReplySC она передается как параметр. Это общее правило передачи функций в другие функции в качестве параметров. Указал скобки и передал в них ее параметр (параметры) - функция исполнится немедленно. Передал только имя, ну и что с того. Вызов функции сделает тот, кто получил ее имя.

Функциональность, реализуемая через AJAX, позволяет выполнить вызов функции JavaScript через данные, полученные от сервера. Фактически, посылая запрос на сервер, та или иная функция может вовсе и не «знать», к какой функции она обращается и с какой информацией.

Выход из функции и ее результат

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

Если требуется, чтобы функция возвращала результат, можно воспользоваться оператором возврата JavaScript: return. В теле функции может быть достаточное количество операторов возврата. Совершенно не обязательно, что все они будут возвращать результат одного и того же типа.

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

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

Аргументы функций

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

Внутри функции доступна переменная arguments, имеющая свойство length. Можно обращаться к любому аргументу функции через arguments , arguments , ... до последнего arguments .

Изменение аргумента функции действительно внутри функции, но не вне ее. Для того чтобы что-то изменить вне функции, нужно воспользоваться оператором JavaScript return, через который передать необходимое значение наружу.

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

У arguments есть свойство callee, которое предназначено для вызова функции, что выполняется в данный момент времени. Если вызвать саму себя, то вариант JavaScript функция в функции позволит реализовать рекурсию.

Использование функций

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

Одна функция может обслуживать несколько элементов страницы и несколько событий. Посредством параметра «this» можно передать функции информацию, откуда она была вызвана.

Классическое использование JS-функций - обработчики событий на элементах. В данном примере в форме входа/выхода посетителя будет вызвана функция scfWecomeGo() или scfWelcomeCancel(), а при выборе режима работы scfMenuItemClick(this).

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

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

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

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

О распределенном мышлении

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

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

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

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

Например, при удалении строки нижние должны сместиться вверх. Для этого потребуется либо сделать новую выборку, что банально и емко по ресурсам, либо пересчитать строчки, использовать на javascript функции массивов и элегантно достигнуть цели.

Аргументы и результаты функций

JavaScript позволяет привести код к «полнофункциональному» состоянию. Нормально, когда аргументом функции является функция. Допускается вариант, когда функция возвращает функцию. JavaScript относится к этому совершенно спокойно.

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

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

Забота разработчика понятна и проста. Есть задача, нужно решение, а не ошибка вроде «JavaScript error the operation is insecure», чистый экран или остановка всего движка браузера.

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

Исполнение сформированного кода

Реализовать исполнение кода, сформированного в процессе работы другого кода, можно посредством «eval». Это не считается отличным решением, но часто можно не усложнять код излишними функциями, а ограничиться банальным формированием строки JavaScript кода и попросту исполнить ее.

В данном примере формируется строчка вставки в действующий див некоторой информации. Номер дива и содержание информации различны для различных позиций, потому такое решение в данной ситуации гарантированно не обеспечит ситуацию «javascript error the operation is insecure», но надежно даст нужный эффект.

Нюанс парадигмы JavaScript «функция в функции»

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

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

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

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

Таких примеров можно привести множество, и все это будут реальные и насущные задачи, вовсе не из области программирования. Именно поэтому проблема заключается именно в том, что без излишеств не обойтись, созданная система функций, точнее ее отладка и последующая надежная работа становится заботой не JavaScript, а разработчика.

24 мая 2011 в 01:13 Пять способов вызвать функцию
  • JavaScript
  • Перевод

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

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

Давайте напишем простую функцию, которая возвращает массив из трех элементов - текущего значения this и двух аргументов, переданных в функцию.
function makeArray(arg1, arg2){ return [ this, arg1, arg2 ]; }

Самый распространенный способ: глобальный вызов Новички часто объявляют функции так, как показано в примере выше. Вызвать эту функцию не составляет труда:
makeArray("one", "two"); // => [ window, "one", "two" ]
Погодите. Откуда взялся объект window ? Почему это у нас this равен window ?

В JavaScript, неважно, выполняется ли скрипт в браузере или в ином окружении, всегда определен глобальный объект . Любой код в нашем скрипте, не «привязанный» к чему-либо (т.е. находящийся вне объявления объекта) на самом деле находится в контексте глобального объекта. В нашем случае, makeArray - не просто функция, «гуляющая» сама по себе. На самом деле, makeArray - метод глобального объекта (в случае исполнения кода в браузере) window . Доказать это легко:
alert(typeof window.methodThatDoesntExist); // => undefined alert(typeof window.makeArray); // => function
То есть вызов makeArray("one", "two"); равносилен вызову window.makeArray("one", "two"); .

Меня печалит тот факт, что этот способ вызова функций наиболее распространен, ведь он подразумевает наличие глобальной функции. А мы все знаем, что глобальные функции и переменные - не самый хороший тон в программировании. Особенно это справедливо для JavaScript. Избегайте глобальных определений, и не пожалеете.

Правило вызова функций №1: Если функция вызывается напрямую, без указания объекта (например, myFunction()), значением this будет глобальный объект (window в случае исполнения кода в браузере).

Вызов метода Давайте создадим простой объект и сделаем makeArray его методом. Объект объявим с помощью литеральной нотации, а после вызовем наш метод:
// создаем объект var arrayMaker = { someProperty: "какое-то значение", make: makeArray }; // вызываем метод make() arrayMaker.make("one", "two"); // => [ arrayMaker, "one", "two" ] // альтернативный синтаксис, используем квадратные скобки arrayMaker["make"]("one", "two"); // => [ arrayMaker, "one", "two" ]
Видите разницу? Значение this в этом случае - сам объект. Почему не window , как в предыдущем случае, ведь объявление функции не изменилось? Весь секрет в том, как передаются функции в JavaScript. Function - это стандартный тип JavaScript, являющийся на самом деле объектом, и как и любой другой объект, функции можно передавать и копировать. В данном случае, мы как бы скопировали всю функцию, включая список аргументов и тело, и присвоили получившийся объект свойству make объекта arrayMaker . Это равносильно такому объявлению:
var arrayMaker = { someProperty: "Какое-то значение"; make: function (arg1, arg2) { return [ this, arg1, arg2]; } };
Правило вызова функций №2: В функции, вызванной с использованием синтаксиса вызова метода, например, obj.myFunction() или obj["myFunction"]() , this будет иметь значение obj .

Непонимание этого простого, в общем-то, принципа часто приводит к ошибкам при обработке событий:
function buttonClicked(){ var text = (this === window) ? "window" : this.id; alert(text); } var button1 = document.getElementById("btn1"); var button2 = document.getElementById("btn2"); button1.onclick = buttonClicked; button2.onclick = function(){ buttonClicked(); };
Щелчок по первой кнопке покажет сообщение «btn1» , потому что в данном случае мы вызываем функцию как метод, и this внутри функции получит значение объекта, которому этот метод принадлежит. Щелчок по второй кнопке выдаст «window» , потому что в этом случае мы вызываем buttonClicked напрямую (т.е. не как obj.buttonClicked()). То же самое происходит, когда мы назначаем обработчик события в тэге элемента, как в случае третьей кнопки. Щелчок по третьей кнопке покажет то же самое сообщение, что и для второй.

При использовании библиотек вроде jQuery думать об этом не надо. jQuery позаботится о том, чтобы переписать значение this в обработчике события так, чтобы значением this был элемент, вызвавший событие:
// используем jQuery $("#btn1").click(function() { alert(this.id); // jQuery позаботится о том, чтобы "this" являлась кнопкой });
Каким образом jQuery удается изменить значение this ? Читайте ниже.

Еще два способа: apply() и call() Логично, что чем чаще вы используете функции, тем чаще вам приходится передавать их и вызывать в разных контекстах. Зачастую возникает необходимость переопределить значение this . Если вы помните, функции в JavaScript являются объектами. На практике это означает, что у функций есть предопределенные методы. apply() и call() - два из них. Они позволяют переопределять значение this:
var car = { year: 2008, model: "Dodge Bailout" }; makeArray.apply(car, [ "one", "two" ]); // => [ car, "one", "two" ] makeArray.call(car, "one", "two"); // => [ car, "one", "two" ]
Эти два метода очень похожи. Первый параметр переопределяет this . Различия между ними заключаются в последющих аргументах: Function.apply() принимает массив значений, которые будут переданы функции, а Function.call() принимает аргументы раздельно. На практике, по моему мнению, удобнее применять apply() .

Правило вызова функций №3: Если требуется переопределить значение this , не копируя функцию в другой объект, можно использовать myFunction.apply(obj) или myFunction.call(obj) .

Конструкторы Я не буду подробно останавливаться на объявлении собственных типов в JavaScript, но считаю необходимым напомнить, что в JavaScript нет классов, а любой пользовательский тип нуждается в конструкторе. Кроме того, методы пользовательского типа лучше объявлять через prototype , который является свойством фукции-конструктора. Давайте создадим свой тип:
// объявляем конструктор function ArrayMaker(arg1, arg2) { this.someProperty = "неважно"; this.theArray = [ this, arg1, arg2 ]; } // объявляем методы ArrayMaker.prototype = { someMethod: function () { alert("Вызван someMethod"); }, getArray: function () { return this.theArray; } }; var am = new ArrayMaker("one", "two"); var other = new ArrayMaker("first", "second"); am.getArray(); // => [ am, "one", "two" ]
Важным в этом примере является наличие оператора new перед вызовом функции. Если бы не он, это был бы глобальный вызов, и создаваемые в конструкторе свойства относились бы к глобальному объекту. Нам такого не надо. Кроме того, в конструкторах обычно не возвращают значения явно. Без оператора new конструктор вернул бы undefined , с ним он возвращает this . Хорошим стилем считается наименование конструкторов с заглавной буквы; это позволит вспомнить о необходимости оператора new .

В остальном, код внутри конструктора, скорее всего, будет похож на код, который вы написали бы на другом языке. Значение this в данном случае - это новый объект, который вы создаете.

Правило вызова функций №4: При вызове функции с оператором new , значением this будет новый объект, созданный средой исполнения JavaScript. Если эта функция не возвращает какой-либо объект явно, будет неявно возвращен this .

Заключение Надеюсь, понимание разницы между разными способами вызова функций возволит вам улучшить ваш JavaScript-код. Иногда непросто отловить ошибки, связанные со значением this , поэтому имеет смысл предупреждать их возникновение заранее.
  • Перевод

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

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

Давайте напишем простую функцию, которая возвращает массив из трех элементов - текущего значения this и двух аргументов, переданных в функцию.
function makeArray(arg1, arg2){ return [ this, arg1, arg2 ]; }

Самый распространенный способ: глобальный вызов Новички часто объявляют функции так, как показано в примере выше. Вызвать эту функцию не составляет труда:
makeArray("one", "two"); // => [ window, "one", "two" ]
Погодите. Откуда взялся объект window ? Почему это у нас this равен window ?

В JavaScript, неважно, выполняется ли скрипт в браузере или в ином окружении, всегда определен глобальный объект . Любой код в нашем скрипте, не «привязанный» к чему-либо (т.е. находящийся вне объявления объекта) на самом деле находится в контексте глобального объекта. В нашем случае, makeArray - не просто функция, «гуляющая» сама по себе. На самом деле, makeArray - метод глобального объекта (в случае исполнения кода в браузере) window . Доказать это легко:
alert(typeof window.methodThatDoesntExist); // => undefined alert(typeof window.makeArray); // => function
То есть вызов makeArray("one", "two"); равносилен вызову window.makeArray("one", "two"); .

Меня печалит тот факт, что этот способ вызова функций наиболее распространен, ведь он подразумевает наличие глобальной функции. А мы все знаем, что глобальные функции и переменные - не самый хороший тон в программировании. Особенно это справедливо для JavaScript. Избегайте глобальных определений, и не пожалеете.

Правило вызова функций №1: Если функция вызывается напрямую, без указания объекта (например, myFunction()), значением this будет глобальный объект (window в случае исполнения кода в браузере).

Вызов метода Давайте создадим простой объект и сделаем makeArray его методом. Объект объявим с помощью литеральной нотации, а после вызовем наш метод:
// создаем объект var arrayMaker = { someProperty: "какое-то значение", make: makeArray }; // вызываем метод make() arrayMaker.make("one", "two"); // => [ arrayMaker, "one", "two" ] // альтернативный синтаксис, используем квадратные скобки arrayMaker["make"]("one", "two"); // => [ arrayMaker, "one", "two" ]
Видите разницу? Значение this в этом случае - сам объект. Почему не window , как в предыдущем случае, ведь объявление функции не изменилось? Весь секрет в том, как передаются функции в JavaScript. Function - это стандартный тип JavaScript, являющийся на самом деле объектом, и как и любой другой объект, функции можно передавать и копировать. В данном случае, мы как бы скопировали всю функцию, включая список аргументов и тело, и присвоили получившийся объект свойству make объекта arrayMaker . Это равносильно такому объявлению:
var arrayMaker = { someProperty: "Какое-то значение"; make: function (arg1, arg2) { return [ this, arg1, arg2]; } };
Правило вызова функций №2: В функции, вызванной с использованием синтаксиса вызова метода, например, obj.myFunction() или obj["myFunction"]() , this будет иметь значение obj .

Непонимание этого простого, в общем-то, принципа часто приводит к ошибкам при обработке событий:
function buttonClicked(){ var text = (this === window) ? "window" : this.id; alert(text); } var button1 = document.getElementById("btn1"); var button2 = document.getElementById("btn2"); button1.onclick = buttonClicked; button2.onclick = function(){ buttonClicked(); };
Щелчок по первой кнопке покажет сообщение «btn1» , потому что в данном случае мы вызываем функцию как метод, и this внутри функции получит значение объекта, которому этот метод принадлежит. Щелчок по второй кнопке выдаст «window» , потому что в этом случае мы вызываем buttonClicked напрямую (т.е. не как obj.buttonClicked()). То же самое происходит, когда мы назначаем обработчик события в тэге элемента, как в случае третьей кнопки. Щелчок по третьей кнопке покажет то же самое сообщение, что и для второй.

При использовании библиотек вроде jQuery думать об этом не надо. jQuery позаботится о том, чтобы переписать значение this в обработчике события так, чтобы значением this был элемент, вызвавший событие:
// используем jQuery $("#btn1").click(function() { alert(this.id); // jQuery позаботится о том, чтобы "this" являлась кнопкой });
Каким образом jQuery удается изменить значение this ? Читайте ниже.

Еще два способа: apply() и call() Логично, что чем чаще вы используете функции, тем чаще вам приходится передавать их и вызывать в разных контекстах. Зачастую возникает необходимость переопределить значение this . Если вы помните, функции в JavaScript являются объектами. На практике это означает, что у функций есть предопределенные методы. apply() и call() - два из них. Они позволяют переопределять значение this:
var car = { year: 2008, model: "Dodge Bailout" }; makeArray.apply(car, [ "one", "two" ]); // => [ car, "one", "two" ] makeArray.call(car, "one", "two"); // => [ car, "one", "two" ]
Эти два метода очень похожи. Первый параметр переопределяет this . Различия между ними заключаются в последющих аргументах: Function.apply() принимает массив значений, которые будут переданы функции, а Function.call() принимает аргументы раздельно. На практике, по моему мнению, удобнее применять apply() .

Правило вызова функций №3: Если требуется переопределить значение this , не копируя функцию в другой объект, можно использовать myFunction.apply(obj) или myFunction.call(obj) .

Конструкторы Я не буду подробно останавливаться на объявлении собственных типов в JavaScript, но считаю необходимым напомнить, что в JavaScript нет классов, а любой пользовательский тип нуждается в конструкторе. Кроме того, методы пользовательского типа лучше объявлять через prototype , который является свойством фукции-конструктора. Давайте создадим свой тип:
// объявляем конструктор function ArrayMaker(arg1, arg2) { this.someProperty = "неважно"; this.theArray = [ this, arg1, arg2 ]; } // объявляем методы ArrayMaker.prototype = { someMethod: function () { alert("Вызван someMethod"); }, getArray: function () { return this.theArray; } }; var am = new ArrayMaker("one", "two"); var other = new ArrayMaker("first", "second"); am.getArray(); // => [ am, "one", "two" ]
Важным в этом примере является наличие оператора new перед вызовом функции. Если бы не он, это был бы глобальный вызов, и создаваемые в конструкторе свойства относились бы к глобальному объекту. Нам такого не надо. Кроме того, в конструкторах обычно не возвращают значения явно. Без оператора new конструктор вернул бы undefined , с ним он возвращает this . Хорошим стилем считается наименование конструкторов с заглавной буквы; это позволит вспомнить о необходимости оператора new .

В остальном, код внутри конструктора, скорее всего, будет похож на код, который вы написали бы на другом языке. Значение this в данном случае - это новый объект, который вы создаете.

Правило вызова функций №4: При вызове функции с оператором new , значением this будет новый объект, созданный средой исполнения JavaScript. Если эта функция не возвращает какой-либо объект явно, будет неявно возвращен this .

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

Лучшие статьи по теме