JavaScript | Функции
185Веб-программирование — JavaScript — Функции
Функция — это блок программного кода на языке JavaScript, который определяется один раз и может выполняться, или вызываться, многократно. Возможно, вы уже знакомы с понятием «функция» под другим названием, таким как подпрограмма, или процедура. Функции могут иметь параметры: определение функции может включать список идентификаторов, которые называются параметрами и играют роль локальных переменных в теле функции.
При вызове функций им могут передаваться значения, или аргументы, соответствующие их параметрам. Функции часто используют свои аргументы для вычисления возвращаемого значения, которое является значением выражения вызова функции. В дополнение к аргументам при вызове любой функции ей передается еще одно значение, определяющее контекст вызова — значение в ключевом слове this.
Функции в языке JavaScript являются объектами и могут использоваться разными способами. Например, функции могут присваиваться переменным и передаваться другим функциям. Поскольку функции являются объектами, имеется возможность присваивать значения их свойствам и даже вызывать их методы.
В JavaScript допускается создавать определения функций, вложенные в другие функции, и такие функции будут иметь доступ ко всем переменным, присутствующим в области видимости определения.
Определение функций
Определение функции начинается с ключевого слова function, за которым указываются следующие компоненты:
- Идентификатор, определяющий имя функции
Имя является обязательной частью инструкции объявления функции: оно будет использовано для создания новой переменной, которой будет присвоен объект новой функции. В выражениях определения функций имя может отсутствовать: при его наличии имя будет ссылаться на объект функции только в теле самой функции.
- Пара круглых скобок вокруг списка из нуля или более идентификаторов, разделенных запятыми
Эти идентификаторы будут определять имена параметров функции и в теле функции могут использоваться как локальные переменные.
- Пара фигурных скобок с нулем или более инструкций JavaScript внутри
Эти инструкции составляют тело функции: они выполняются при каждом вызове функции.
В следующем примере показано несколько определений функций в виде инструкций и выражений. Обратите внимание, что определения функций в виде выражений удобно использовать, только если они являются частью более крупных выражений, таких как присваивание или вызов функции, которые выполняют нек
professorweb.ru
функции / RUVDS.com corporate blog / Habr
Сегодня публикуем четвёртую часть перевода руководства по JavaScript, которая посвящена функциям.→ Часть 1: первая программа, особенности языка, стандарты
→ Часть 2: стиль кода и структура программ
→ Часть 3: переменные, типы данных, выражения, объекты
→ Часть 4: функции
→ Часть 5: массивы и циклы
→ Часть 7: строгий режим, ключевое слово this, события, модули, математические вычисления
→ Часть 8: обзор возможностей стандарта ES6
→ Часть 9: обзор возможностей стандартов ES7, ES8 и ES9
Функции в JavaScript
Поговорим о функциях в JavaScript, сделаем их общий обзор и рассмотрим подробности о них, знание которых позволит вам эффективно ими пользоваться.
Функция — это самостоятельный блок кода, который можно, один раз объявив, вызывать столько раз, сколько нужно. Функция может, хотя это и необязательно, принимать параметры. Функции возвращают единственное значение.
Function
. Их ключевое отличие от обычных объектов, дающее им те исключительные возможности, которыми они обладают, заключается в том, что функции можно вызывать.Кроме того, функции в JavaScript называют «функциями первого класса» так как их можно назначать переменным, их можно передавать другим функциям в качестве аргументов, их можно возвращать из других функций.
Сначала рассмотрим особенности работы с функциями и соответствующие синтаксические конструкции, которые существовали в языке до появления стандарта ES6 и актуальны до сих пор.
Вот как выглядит объявление функции (function declaration).
function doSomething(foo) {
//сделать что-нибудь
}
В наши дни такие функции называют «обычными», отличая их от «стрелочных» функций, которые появились в ES6.
Функцию можно назначить переменной или константе. Такая конструкция называется функциональным выражением (function expression).
const doSomething = function(foo) {
//сделать что-нибудь
}
Можно заметить, что в вышеприведённом примере функция назначена константе, но сама она имени не имеет. Такие функции называют анонимными. Подобным функциям можно назначать имена. В таком случае речь идёт об именованном функциональном выражении (named function expression).
const doSomething = function doSomFn(foo) { //сделать что-нибудь }
Использование таких выражений повышает удобство отладки (в сообщениях об ошибках, где проводится трассировка стека, видно имя функции). Имя функции в функциональном выражении может понадобиться и для того, чтобы функция могла бы сама себя вызывать, без чего не обойтись при реализации рекурсивных алгоритмов.
В стандарте ES6 появились стрелочные функции (arrow function), которые особенно удобно использовать в виде так называемых «встроенных функций» (inline function) — в роли аргументов, передаваемых другим функциям (коллбэков).
const doSomething = foo => { //сделать что-нибудь }
Стрелочные функции, помимо того, что структуры, используемые для их объявления, получаются более компактными, чем при использовании обычных функций, отличаются от них некоторыми важными особенностями, о которых мы поговорим ниже.
Параметры функций
Параметры представляют собой переменные, которые задаются на этапе объявления функции и будут содержать передаваемые ей значения (эти значения называют аргументами). Функции в JavaScript могут либо не иметь параметров, либо иметь один или несколько параметров.
const doSomething = () => { //сделать что-нибудь } const doSomethingElse = foo => { //сделать что-нибудь } const doSomethingElseAgain = (foo, bar) => { //сделать что-нибудь }
Здесь показано несколько примеров стрелочных функций.
Начиная со стандарта ES6 у функций могут быть так называемые «параметры по умолчанию» (default parameters).
const doSomething = (foo = 1, bar = 'hey') => {
//сделать что-нибудь
}
Они представляют собой стандартные значения, задаваемые параметрам функций в том случае, если при её вызове значения некоторых параметров не задаются. Например, функцию, показанную выше, можно вызвать как с передачей ей всех двух принимаемых ей параметров, так и другими способами.
doSomething(3)
doSomething()
В ES8 появилась возможность ставить запятую после последнего аргумента функции (это называется trailing comma). Эта возможность позволяет повысить удобство редактирования кода при использовании систем контроля версий в ходе разработки программ. Подробности об этом можно почитать здесь и здесь.
const doSomething = (foo = 1, bar = 'hey') => {
//сделать что-нибудь
}
const args = [2, 'ho!']
doSomething(...args)
Если функции нужно принимать много параметров, то запомнить порядок их следования может быть непросто. В таких случаях используются объекты с параметрами и возможности по деструктурированию объектов ES6.
const doSomething = ({ foo = 1, bar = 'hey' }) => { //сделать что-нибудь console.log(foo) // 2 console.log(bar) // 'ho!' } const args = { foo: 2, bar: 'ho!' } doSomething(args)
Этот приём позволяет, описывая параметры в виде свойств объекта и передавая функции объект, получить в функции доступ к параметрам по их именам без использования дополнительных конструкций. Подробнее об этом приёме можно почитать здесь.
Значения, возвращаемые из функций
Все функции возвращают некое значение. Если команда возврата явно не задана — функция возвратит
undefined
.const doSomething = (foo = 1, bar = 'hey') => { //сделать что-нибудь } console.log(doSomething())
Выполнение функции завершается либо после того, как оказывается выполненным весь код, который она содержит, либо после того, как в коде встречается ключевое слово
return
. Когда в функции встречается это ключевое слово, её работа завершается, а управление передаётся в то место, откуда была вызвана функция.Если после ключевого слова return
указать некое значение, то это значение возвращается в место вызова функции в качестве результата выполнения этой функции.
const doSomething = () => { return 'test' } const result = doSomething() // result === 'test'
Из функции можно возвращать лишь одно значение. Для того чтобы получить возможность возврата нескольких значений, возвращать их можно либо в виде объекта, используя объектный литерал, либо в виде массива, а при вызове функции применять конструкцию деструктурирующего присваивания. Имена параметров при этом сохраняются. При этом, если нужно работать с объектом или массивом, возвращённым из функции, именно в виде объекта или массива, можно обойтись без деструктурирующего присваивания.
const doSomething = () => {
return ['Roger', 6]
}
const [ name, age ] = doSomething()
console.log(name, age) //Roger 6
Конструкцию
const [ name, age ] = doSomething()
можно прочитать следующим образом: «объявить константы name
и age
и присвоить им значения элементов массива, который возвратит функция».Вот как то же самое выглядит с использованием объекта.
const doSomething = () => {
return {name: 'Roger', age: 6}
}
const { name, age } = doSomething()
console.log(name, age) //Roger 6
Вложенные функции
Функции можно объявлять внутри других функций.
const doSomething = () => {
const doSomethingElse = () => {}
doSomethingElse()
return 'test'
}
doSomething()
Область видимости вложенной функции ограничена внешней по отношению к ней функцией, её нельзя вызвать извне.
Методы объектов
Когда функции используются в качестве свойств объектов, такие функции называют методами объектов.
const car = {
brand: 'Ford',
model: 'Fiesta',
start: function() {
console.log(`Started`)
}
}
car.start()
Ключевое слово this
Если сравнить стрелочные и обычные функции, используемые в качестве методов объектов, можно обнаружить их важное различие, заключающееся в смысле ключевого слова
this
. Рассмотрим пример.const car = {
brand: 'Ford',
model: 'Fiesta',
start: function() {
console.log(`Started ${this.brand} ${this.model}`)
},
stop: () => {
console.log(`Stopped ${this.brand} ${this.model}`)
}
}
car.start() //Started Ford Fiesta
car.stop() //Stopped undefined undefined
Как видно, вызов метода
start()
приводит ко вполне ожидаемому результату, а вот метод stop()
явно работает неправильно.Происходит это из-за того, что ключевое слово this
по-разному ведёт себя при его использовании в стрелочных и обычных функциях. А именно, ключевое слово this
в стрелочной функции содержит ссылку на контекст, включающий в себя функцию. В данном случае, если речь идёт о браузере, этим контекстом является объект window
.
Вот как выглядит выполнение такого кода в консоли браузера.
const test = {
fn: function() {
console.log(this)
},
arrFn: () => {
console.log(this)
}
}
test.fn()
test.arrFn()
Особенности ключевого слова this в обычных и стрелочных функциях
Как можно заметить, обращение к this в обычной функции означает обращение к объекту, а this
в стрелочной функции указывает на window
.
Всё это означает, что стрелочные функции не подходят на роль методов объектов и конструкторов (если попытаться использовать стрелочную функцию в роли конструктора — будет выдана ошибка TypeError
).
Немедленно вызываемые функциональные выражения
Немедленно вызываемое функциональное выражение (Immediately Invoked Function Expression, IIFE) — это функция, которая автоматически вызывается сразу после её объявления.
;(function () {
console.log('executed')
})()
Точка с запятой перед IIFE необязательна, но её использование позволяет застраховаться от ошибок, связанных с автоматической расстановкой точек с запятой.
В вышеприведённом примере в консоль попадёт слово executed
, после чего IIFE завершит работу. IIFE, точно так же как и другие функции, могут возвращать результаты своей работы.
const something = (function () {
return 'IIFE'
})()
console.log(something)
После выполнения этого простого примера в консоль попадёт строка
IIFE
, которая оказалась в константе something
после выполнения немедленно вызываемого функционального выражения. Может показаться, что особой пользы от такой конструкции нет. Однако если в IIFE выполняются некие сложные вычисления, которые нужно выполнить лишь однажды, после чего соответствующие механизмы оказываются ненужными — полезность IIFE оказывается очевидной. А именно, при таком подходе после выполнения IIFE в программе будет доступен лишь возвращённый функцией результат. Кроме того, можно вспомнить, что функции способны возвращать другие функции и объекты. Речь идёт о замыканиях, о них мы поговорим ниже.Поднятие функций
Перед выполнением JavaScript-кода производится его реорганизация. Мы уже говорили о механизме поднятия (hoisting) переменных, объявленных с использованием ключевого слова
var
. Похожий механизм действует и при работе с функциями. А именно, речь идёт о том, что объявления функций в ходе обработки кода перед его выполнением перемещаются в верхнюю часть их области видимости. В результате, например, оказывается, что вызвать функцию можно до её объявления.doSomething() //did something
function doSomething() {
console.log('did something')
}
Если переместить вызов функции так, чтобы он шёл после её объявления, ничего не изменится.
Если же в похожей ситуации воспользоваться функциональным выражением, то похожий код выдаст ошибку.
doSomething() //TypeError
var doSomething = function () {
console.log('did something')
}
В данном случае оказывается, что хотя объявление переменной
doSomething
и поднимается в верхнюю часть области видимости, это не относится к операции присваивания.Если вместо
var
в похожей ситуации использовать ключевые слова let
или const
, такой код тоже работать не будет, правда, система выдаст другое сообщение об ошибке (ReferenceError
а не TypeError
), так как при использовании let
и const
объявления переменных и констант не поднимаются.Стрелочные функции
Сейчас мы подробнее поговорим о стрелочных функциях, с которыми мы уже встречались. Их можно считать одним из наиболее значительных новшеств стандарта ES6, они отличаются от обычных функций не только внешним видом, но и особенностями поведения. В наши дни они используются чрезвычайно широко. Пожалуй, нет ни одного современного проекта, где они не использовались бы в подавляющем большинстве случаев. Можно сказать, что их появление навсегда изменило и внешний вид JS-кода и особенности его работы.
С чисто внешней точки зрения синтаксис объявления стрелочных функций оказывается компактнее синтаксиса обычных функций. Вот объявление обычной функции.
const myFunction = function () {
//...
}
Вот объявление стрелочной функции, которое, в целом, если не учитывать особенности стрелочных функций, аналогично предыдущему.
const myFunction = () => {
//...
}
Если тело стрелочной функции содержит лишь одну команду, результат которой возвращает эта функция, его можно записать без фигурных скобок и без ключевого слова
return
. Например, такая функция возвращает сумму переданных ей аргументов.const myFunction = (a,b) => a + b
console.log(myFunction(1,2)) //3
Как видите, параметры стрелочных функций, как и в случае с обычными функциями, описывают в скобках. При этом, если такая функция принимает всего один параметр, его можно указать без скобок. Например, вот функция, которая возвращает результат деления переданного ей числа на 2.
const myFunction = a => a / 2
console.log(myFunction(8)) //4
В результате оказывается, что стрелочные функции очень удобно использовать в ситуациях, в которых нужны маленькие функции.
▍Неявный возврат результатов работы функции
Мы уже касались этой особенности стрелочных функций, но она настолько важна, что её следует обсудить подробнее. Речь идёт о том, что однострочные стрелочные функции поддерживают неявный возврат результатов своей работы. Пример возврата примитивного значения из однострочной стрелочной функции мы уже видели. Как быть, если такая функция должна возвратить объект? В таком случае фигурные скобки объектного литерала могут запутать систему, поэтому в теле функции используются круглые скобки.
const myFunction = () => ({value: 'test'})
const obj = myFunction()
console.log(obj.value) //test
▍Ключевое слово this и стрелочные функции
Выше, когда мы рассматривали особенности ключевого слова
this
, мы сравнивали обычные и стрелочные функции. Этот раздел призван обратить ваше внимание на важность их различий. Ключевое слово this
, само по себе, может вызывать определённые сложности, так как оно зависит и от контекста выполнения кода, и от того, включен или нет строгий режим (strict mode).Как мы уже видели, при использовании ключевого слова this
в методе объекта, представленного обычной функцией, this
указывает на объект, которому принадлежит метод. В таком случае говорят о привязке ключевого слова this
к значению, представляющему собой контекст выполнения функции. В частности, если функция вызвана в виде метода объекта, то ключевое слово this
привязано к этому объекту.
В случае же со стрелочными функциями оказывается так, что в них привязка this
не выполняется, они пользуются ключевым словом this
из содержащих их областей видимости. В результате их не рекомендуется использовать в качестве методов объектов.
Та же самая проблема возникает и при использовании функций в качестве обработчиков событий элементов DOM. Например, HTML-элемент button
используют для описания кнопок. Событие click
вызывается при щелчке мышью по кнопке. Для того чтобы отреагировать на это событие в коде, нужно сначала получить ссылку на соответствующий элемент, а потом назначить ему обработчик события click
в виде функции. В качестве такого обработчика можно использовать и обычную функцию, и стрелочную. Но, если в обработчике событий нужно обращаться к тому элементу, для которого оно вызвано (то есть — к this
), стрелочная функция тут не подойдёт, так как доступное в ней значение this
указывает на объект window
. Для того чтобы проверить это на практике, создайте HTML-страницу, код которой показан ниже, и понажимайте на кнопки.
<!DOCTYPE html>
<html>
<body>
<button>Function</button>
<button>Arrow function</button>
<script>
const f = document.getElementById("fn")
f.addEventListener('click', function () {
alert(this === f)
})
const af = document.getElementById("arrowFn")
af.addEventListener('click', () => {
alert(this === window)
})
</script>
</body>
</html>
В данном случае при нажатии на эти кнопки будут появляться окна, содержащие
true
. Однако в обработчике события click
кнопки с идентификатором fn
проверяется равенство this
самой кнопке, а в кнопке с идентификатором arrowFn
проверяется равенство this
и объекта window
.В результате, если в обработчике события HTML-элемента нужно обращаться к this
, стрелочная функция для оформления такого обработчика не подойдёт.
Замыкания
Замыкания — это важная концепция в JavaScript. Фактически, если вы писали JS-функции, то вы пользовались и замыканиями. Замыкания применяются в некоторых паттернах проектирования — в том случае, если нужно организовать строгий контроль доступа к неким данным или функциям.
Когда функция вызывается, у неё есть доступ ко всему тому, что находится во внешней по отношению к ней области видимости. Но к тому, что объявлено внутри функции, извне доступа нет. То есть, если в функции была объявлена некая переменная (или другая функция), они недоступны внешнему коду ни во время выполнения функции, ни после завершения её работы. Однако если из функции возвратить другую функцию, то эта новая функция будет иметь доступ ко всему тому, что было объявлено в исходной функции. При этом всё это будет скрыто от внешнего кода в замыкании.
Рассмотрим пример. Вот функция, которая принимает имя собаки, после чего выводит его в консоль.
const bark = dog => {
const say = `${dog} barked!`
;(() => console.log(say))()
}
bark(`Roger`) // Roger barked!
Значение, возвращаемое этой функцией нас пока не интересует, текст выводится в консоль с помощью IIFE, что в данном случае особой роли не играет, однако, это поможет нам увидеть связь между этой функцией и её вариантом, в котором, вместо вызова функции, которая выводит текст в консоль, мы эту функцию из переписанной функции
bark()
возвратим.const prepareBark = dog => {
const say = `${dog} barked!`
return () => console.log(say)
}
const bark = prepareBark(`Roger`)
bark() // Roger barked!
Результат работы код в двух случаях оказывается одинаковым. Но во втором случае то, что было передано исходной функции при её вызове (имя собаки,
Roger
), хранится в замыкании, после чего используется другой функцией, возвращённой из исходной.Проведём ещё один эксперимент — создадим, пользуясь исходной функцией, две новых, для разных собак.
const prepareBark = dog => {
const say = `${dog} barked!`
return () => {
console.log(say)
}
}
const rogerBark = prepareBark(`Roger`)
const sydBark = prepareBark(`Syd`)
rogerBark()
sydBark()
Этот код выведет следующее.
Roger barked!
Syd barked!
Оказывается, что значение константы
say
привязано к функции, которая возвращена из функции prepareBark()
.Обратите внимание на то, что say
, при повторном вызове prepareBark()
, получает новое значение, при этом значение, записанное в say
при первом вызове prepareBark()
, не меняется. Речь идёт о том, что при каждом вызове этой функции создаётся новое замыкание.
Итоги
Сегодня мы говорили об обычных и стрелочных функциях, об особенностях их объявления и использования, о том, как, в разных ситуациях ведёт себя ключевое слово
this
, и о замыканиях. В следующий раз обсудим массивы и циклы.Уважаемые читатели! Как вы относитесь к стрелочным функциям в JavaScript?
habr.com
Четыре паттерна вызова функций в JavaScript / Habr
Язык JavaScript был представлен как язык функционального программирования. Причина заключается в том, что функции в JS не просто разделяют логику на операционные блоки, функции являются объектами первого класса, способными создавать другие объекты. Подобная зависимость от функций одновременно является как сильной стороной, так и настоящим проклятием этого языка. Сильная сторона заключается в том, что язык, обладая подобными особенностями, становится легковесным и быстрым (каким 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. Надеюсь что этот пост вам поможет.
habr.com