Язык JavaScript был представлен как язык функционального программирования. Причина заключается в том, что функции в JS не просто разделяют логику на операционные блоки, функции являются объектами первого класса, способными создавать другие объекты. Подобная зависимость от функций одновременно является как сильной стороной, так и настоящим проклятием этого языка. Сильная сторона заключается в том, что язык, обладая подобными особенностями, становится легковесным и быстрым (каким JavaScript изначально и видели его создатели). Однако если вы не знаете что делаете — однозначно ждите беды.
Я предлагаю посмотреть на паттерны вызова функций, а точнее на то, как значительно изменяется результат в зависимости от выбранного паттерна. Также мы рассмотрим как ведет себя
Итак, существует четыре пути вызова функций:
JavaScript, как и все современные языки, может модулировать логику внутри функций, и эти функции могут быть вызваны в любой момент, посреди уже запущенного процесса. Вызвав функцию, мы передаем ей необходимые параметры и управление процессами, останавливая текущую операцию. Оператор вызова — круглые скобки (), которые могу заключать в себе параметры, разделенные через запятую.
К сожалению, существует несколько паттернов для вызова функций. О них не нужно быть в курсе. Их нужно зазубрить и понять, потому как, в зависимости от выбранного паттерна, вы получите разные результаты. На мой взгляд, данная особенность является ошибкой в проектировании самого языка, и если бы JavaScript создавался в меньшей спешке и с большим вниманием, различных проблем подобного характера удалось бы избежать.
Как уже говорилось, оператор для вызова функции один, а способов вызова — четыре.
Когда функция является частью объекта, она называется методом. «Вызов метода» представляет из себя вызов функции, принадлежащей объекту. Пример:
В «вызове метода» значение
Вызов функции выполняется с помощью оператора ():
Используя данный паттерн,
Как думаете, что будет выведено на экран? Если вы решили, что 1 — вы ошибаетесь (однако не стоит винить себя — вините кривоватый дизайн JavaScript). Правильный ответ — 500. Обратите внимание,
Можно легко обойти эту проблему путем создания переменной
Таким образом мы привязали
Предупреждение: это еще одна особенность JavaScript, который сильно отличается от классических языков ООП! Это прототипно-ориентированный язык программирования, однако его создателям показалось, что люди «классической школы» (коих большинство) будут некомфортно себя чувствовать. В результате в прототипный JavaScript добавились принципы классического ООП и получилось что получилось — бардак.
В классическом ООП объект является реализацией класса. В С++ и Java для такой реализации используется оператор
Паттерн запускается путем размещения оператора
Несмотря на то, что
Мы могли бы игнорировать использование
Этот паттерн продуман гораздо лучше остальных. Он позволяет вручную запустить функцию, попутно снабдив ее параметрами и обозначив
Данный паттерн использует два параметра: первый — это объект, к которому привязывается
В примере выше
Этот пример использует
В JavaScript также существует оператор
Хорошо это или не очень, JavaScript вот-вот захватит мир. А потому просто необходимо знать о его особенностях, особенно о тех, которых следует избегать. Понимание четырех паттернов вызова функций — обязаетельное условие для изучающих JavaScript. Надеюсь что этот пост вам поможет.
Я предлагаю посмотреть на паттерны вызова функций, а точнее на то, как значительно изменяется результат в зависимости от выбранного паттерна. Также мы рассмотрим как ведет себя
this
, в зависимости от способа вызова функции. Итак, существует четыре пути вызова функций:
- Вызов метода — Method Invocation
- Вызов функции — Function Invocation
- Вызов конструктора — Constructor Invocation
- Вызов apply и call — Apply And Call Invocation
Выполнение функции
JavaScript, как и все современные языки, может модулировать логику внутри функций, и эти функции могут быть вызваны в любой момент, посреди уже запущенного процесса. Вызвав функцию, мы передаем ей необходимые параметры и управление процессами, останавливая текущую операцию. Оператор вызова — круглые скобки (), которые могу заключать в себе параметры, разделенные через запятую.
К сожалению, существует несколько паттернов для вызова функций. О них не нужно быть в курсе. Их нужно зазубрить и понять, потому как, в зависимости от выбранного паттерна, вы получите разные результаты. На мой взгляд, данная особенность является ошибкой в проектировании самого языка, и если бы JavaScript создавался в меньшей спешке и с большим вниманием, различных проблем подобного характера удалось бы избежать.
Четыре паттерна
Как уже говорилось, оператор для вызова функции один, а способов вызова — четыре.
Вызов метода — Method Invocation
Когда функция является частью объекта, она называется методом. «Вызов метода» представляет из себя вызов функции, принадлежащей объекту. Пример:
var obj = {
value: 0,
increment: function() {
this.value+=1;
}
};
obj.increment();
В «вызове метода» значение
this
будет ссылаться на объект, которому принадлежит функция, в нашем случае на obj, причем данная связь будет установлена после запуска функции, что носит термин позднего привязывания (late binding).Вызов функции — Function Invocation
Вызов функции выполняется с помощью оператора ():
add(2,3); //5
Используя данный паттерн,
this
привязывается к global object. Это, несомненно, является ошибкой языка — постоянная привязка this к глобальному объекту может уничтожить его контекст. Это особенно заметно, если использовать функцию внутри метода. Давайте посмотрим на пример:var value = 500; //Global variable
var obj = {
value: 0,
increment: function() {
this.value++;
var innerFunction = function() {
alert(this.value);
}
innerFunction(); //Function invocation pattern
}
}
obj.increment(); //Method invocation pattern
Как думаете, что будет выведено на экран? Если вы решили, что 1 — вы ошибаетесь (однако не стоит винить себя — вините кривоватый дизайн JavaScript). Правильный ответ — 500. Обратите внимание,
innerFunction
вызывается с использованием вышеупомянутого паттерна «вызова функции», соответственно this
привязывается к global object. В результате мы и получаем 500. Можно легко обойти эту проблему путем создания переменной
this
, но это, по моему мнению, является хаком. var value = 500; //Global variable
var obj = {
value: 0,
increment: function() {
var that = this;
that.value++;
var innerFunction = function() {
alert(that.value);
}
innerFunction(); //Function invocation pattern
}
}
obj.increment();
Таким образом мы привязали
this
к объекту, внутри которого вызывается функция. Вызов конструктора — Constructor Invocation
Предупреждение: это еще одна особенность JavaScript, который сильно отличается от классических языков ООП! Это прототипно-ориентированный язык программирования, однако его создателям показалось, что люди «классической школы» (коих большинство) будут некомфортно себя чувствовать. В результате в прототипный JavaScript добавились принципы классического ООП и получилось что получилось — бардак.
В классическом ООП объект является реализацией класса. В С++ и Java для такой реализации используется оператор
new
. Судя по всему, создатели JS решили не ходить далеко за примером, и реализовать нечто подобное в паттерне «вызов конструктора»…Паттерн запускается путем размещения оператора
new
прямо перед вызовом, например:var Cheese = function(type) {
cheeseType = type;
return cheeseType;
}
cheddar = new Cheese("cheddar"); //Возвращается объект, а не тип
Несмотря на то, что
Cheese
является функциональным объектом (а значит умеет переваривать код), мы создали новый объект путем вызова функции с new
. this
в данном случае будет относиться к свежесозданному объекту, и поведение return
будет изменено. К слову о return. Его использования в «вызове конструктора» имеет две особенности: - если функция возвращает число, цепочку, логическое выражение (true/false), null или undefined,
return
не сработает, a мы получимthis
- если функция возвращает реализацию объекта (то есть все, кроме простых переменных), мы увидим данный объект, а не
this
var obj = {
data : "Hello World"
}
var Func1 = function() {
return obj;
}
var Func2 = function() {
return "I am a simple type";
}
var f1 = new Func1(); //f1 назначается объекту
var f2 = new Func2(); //f2 назначается новому объекту
Мы могли бы игнорировать использование
this
, и назначать объектам литералы, если бы не одно но: создатели JavaScript связали с данным паттерном одну из ключевых возможностей языка — создание объектов с произвольной ссылкой на прототип (подробнее здесь — англ.). Данный паттерн неинтуитивен, более того, с ним часто возникают проблемы. Решение проблемы предлагал Douglas Crockford: можно использовать augment object с методом create. Я рад сообщить, что начиная с версии 1.8.5 JavaScript, Object.create
является вполне работающим инструментом.Вызов apply и call — Apply And Call Invocation
Этот паттерн продуман гораздо лучше остальных. Он позволяет вручную запустить функцию, попутно снабдив ее параметрами и обозначив
this
. Из-за того, что функции у нас являются полноправными объектами, каждая функция в JavaScript связана с Function.prototype, а значит мы можем легко добавлять к ним методы. Данный паттерн использует два параметра: первый — это объект, к которому привязывается
this
, второй — это массив, связанный с параметрами:var add = function(num1, num2) {
return num1+num2;
}
array = [3,4];
add.apply(null,array); //7
В примере выше
this
относится к null
(функция не является объектом), а массив привязан к num1
и num2
. Но продолжим эксперементировать с первым параметром:var obj = {
data:'Hello World'
}
var displayData = function() {
alert(this.data);
}
displayData(); //undefined
displayData.apply(obj); //Hello World
Этот пример использует
apply
для привязки this
к obj
. В результате мы в состоянии получить значение this.data
. Настоящая ценность apply заключается именно в привязывании this
. В JavaScript также существует оператор
call
, похожий на apply
всем, за исключением того что получает не параметры, а список аргументов. Заключение
Хорошо это или не очень, JavaScript вот-вот захватит мир. А потому просто необходимо знать о его особенностях, особенно о тех, которых следует избегать. Понимание четырех паттернов вызова функций — обязаетельное условие для изучающих JavaScript. Надеюсь что этот пост вам поможет.