Функции
Содержание:
- Оператор typeof
- Я знал ответ, это же просто
- Квадратные скобки
- 10) addEvent()
- Остаточные параметры (…)
- JavaScript Функции
- Придумывайте правильные имена
- Function Expressions
- Arrow Functions
- Литералы и свойства
- Синтаксис
- Параметры, которые можно вызвать функцией
- Вызов функции как метода
- Неотложные функции
- 9) onReady()
- Возвращаемые значения
- Усиливаем пример с определением имени. Тесты
- Вложенные функции
- Переходим к нескольким аргументам с «func.apply»
- «this» не является фиксированным
- Вызов функции с помощью конструктора функций
- Итого
Оператор typeof
Оператор возвращает тип аргумента. Это полезно, когда мы хотим обрабатывать значения различных типов по-разному или просто хотим сделать проверку.
У него есть две синтаксические формы:
- Синтаксис оператора: .
- Синтаксис функции: .
Другими словами, он работает со скобками или без скобок. Результат одинаковый.
Вызов возвращает строку с именем типа:
Последние три строки нуждаются в пояснении:
- — это встроенный объект, который предоставляет математические операции и константы. Мы рассмотрим его подробнее в главе Числа. Здесь он служит лишь примером объекта.
- Результатом вызова является . Это официально признанная ошибка в , ведущая начало с времён создания JavaScript и сохранённая для совместимости. Конечно, не является объектом. Это специальное значение с отдельным типом.
- Вызов возвращает , потому что является функцией. Мы изучим функции в следующих главах, где заодно увидим, что в JavaScript нет специального типа «функция». Функции относятся к объектному типу. Но обрабатывает их особым образом, возвращая . Так тоже повелось от создания JavaScript. Формально это неверно, но может быть удобным на практике.
Я знал ответ, это же просто
Если на опрос выше вы ответили, что функция myFunc является анонимной, поздравляю—это правильно! Отметьте этот день красным цветом в календаре, позовите родных и близких, начинайте разливать шампанское по бокалам.
Итак, значит функция в коде блока выше является анонимной:
const myFunc = function() { };
А что, если я тебе скажу, что ты можешь обратится к свойству name, и получить конкретное значение?
myFunc.name // "myFunc"
Полученное значение не пустое, но при этом функция выше, как мы выяснили, анонимная, но это же какой-то понятийный коллапс, товарищи! Не торопитесь уходить в backend-разработку, Шерлок Холмс уже начал свое расследование.
Квадратные скобки
Для свойств, имена которых состоят из нескольких слов, доступ к значению «через точку» не работает:
JavaScript видит, что мы обращаемся к свойству , а затем идёт непонятное слово . В итоге синтаксическая ошибка.
Точка требует, чтобы ключ был именован по правилам именования переменных. То есть не имел пробелов, не начинался с цифры и не содержал специальные символы, кроме и .
Для таких случаев существует альтернативный способ доступа к свойствам через квадратные скобки. Такой способ сработает с любым именем свойства:
Сейчас всё в порядке
Обратите внимание, что строка в квадратных скобках заключена в кавычки (подойдёт любой тип кавычек)
Квадратные скобки также позволяют обратиться к свойству, имя которого может быть результатом выражения. Например, имя свойства может храниться в переменной:
Здесь переменная может быть вычислена во время выполнения кода или зависеть от пользовательского ввода. После этого мы используем её для доступа к свойству. Это даёт нам большую гибкость.
Пример:
Запись «через точку» такого не позволяет:
Мы можем использовать квадратные скобки в литеральной нотации для создания вычисляемого свойства.
Пример:
Смысл вычисляемого свойства прост: запись означает, что имя свойства необходимо взять из переменной .
И если посетитель введёт слово , то в объекте теперь будет лежать свойство .
По сути, пример выше работает так же, как и следующий пример:
…Но первый пример выглядит лаконичнее.
Мы можем использовать и более сложные выражения в квадратных скобках:
Квадратные скобки дают намного больше возможностей, чем запись через точку. Они позволяют использовать любые имена свойств и переменные, хотя и требуют более громоздких конструкций кода.
Подведём итог: в большинстве случаев, когда имена свойств известны и просты, используется запись через точку. Если же нам нужно что-то более сложное, то мы используем квадратные скобки.
10) addEvent()
Несомненно, важнейший инструмент в управлении событиями! Вне зависимости от того, какой версией вы пользуетесь и кем она написана, она делает то, что написано у неё в названии: присоединяет к элементу обработчик события.
function addEvent(elem, evType, fn) { if (elem.addEventListener) { elem.addEventListener(evType, fn, false); } else if (elem.attachEvent) { elem.attachEvent('on' + evType, fn) } else { elem = fn } }
Этот код обладает двумя достоинствами — он простой и кросс-браузерный.
Основной его недостаток — в том, он не передает в обработчик для IE. Точнее, этого не делает .
Для передачи правильного this можно заменить соответствующую строку на:
elem.attachEvent("on"+evType, function() { fn.apply(elem) })
Это решит проблему с передачей , но обработчик никак нельзя будет снять, т.к. должен вызывать в точности ту функцию, которая была передана .
Существует два варианта обхода проблемы:
- Возвращать функцию, использованную для назначения обработчика:
function addEvent(elem, evType, fn) { if (elem.addEventListener) { elem.addEventListener(evType, fn, false) return fn } iefn = function() { fn.call(elem) } elem.attachEvent('on' + evType, iefn) return iefn } function removeEvent(elem, evType, fn) { if (elem.addEventListener) { elem.removeEventListener(evType, fn, false) return } elem.detachEvent('on' + evType, fn) }
Используется так:
function handler() { alert(this) } var fn = addEvent(elem, "click", handler) ... removeEvent(elem, "click", fn)
-
Можно не использовать в обработчике события вообще, а передавать элемент через замыкание:
function handler() { // используем не this, а переменную, ссылающуюся на элемент alert(*!*elem*/!*) } ...
В качестве альтернативы и для примера более серьезной библиотеки обработки событий вы можете рассмотреть статью Кросс-браузерное добавление и обработка событий.
Остаточные параметры (…)
Вызывать функцию можно с любым количеством аргументов независимо от того, как она была определена.
Например:
Лишние аргументы не вызовут ошибку. Но, конечно, посчитаются только первые два.
Остаточные параметры могут быть обозначены через три точки . Буквально это значит: «собери оставшиеся параметры и положи их в массив».
Например, соберём все аргументы в массив :
Мы можем положить первые несколько параметров в переменные, а остальные – собрать в массив.
В примере ниже первые два аргумента функции станут именем и фамилией, а третий и последующие превратятся в массив :
Остаточные параметры должны располагаться в конце
Остаточные параметры собирают все остальные аргументы, поэтому бессмысленно писать что-либо после них. Это вызовет ошибку:
должен всегда быть последним.
JavaScript Функции
Функция (function) – это самостоятельный фрагмент кода, решающий определенную задачу. Каждой функции присваивается уникальное имя, по которому ее можно идентифицировать и «вызвать» в нужный момент.
Функции в языке JavaScript являются объектами, и следовательно ими можно манипулировать как объектами. Например, функции могут присваиваться переменным, элементам массива, свойствам объектов, передавать в качестве аргумента функциям и возвращать в качестве значения из функций.
В JavaScript есть встроенные функции, которые можно использовать в программах, но код которых нельзя редактировать или посмотреть.
Примеры встроенных функций вы уже видели – это , , и . Кроме использования встроенных функций, вы можете создать свои собственные, так называемые пользовательские функции.
Придумывайте правильные имена
В разговоре о переменных необходимо упомянуть, что есть ещё одна чрезвычайно важная вещь.
Название переменной должно иметь ясный и понятный смысл, говорить о том, какие данные в ней хранятся.
Именование переменных – это один из самых важных и сложных навыков в программировании. Быстрый взгляд на имена переменных может показать, какой код был написан новичком, а какой – опытным разработчиком.
В реальном проекте большая часть времени тратится на изменение и расширение существующей кодовой базы, а не на написание чего-то совершенно нового с нуля. Когда мы возвращаемся к коду после какого-то промежутка времени, гораздо легче найти информацию, которая хорошо размечена. Или, другими словами, когда переменные имеют хорошие имена.
Пожалуйста, потратьте время на обдумывание правильного имени переменной перед её объявлением. Делайте так, и будете вознаграждены.
Несколько хороших правил:
- Используйте легко читаемые имена, такие как или .
- Избегайте использования аббревиатур или коротких имён, таких как , , , за исключением тех случаев, когда вы точно знаете, что так нужно.
- Делайте имена максимально описательными и лаконичными. Примеры плохих имён: и . Такие имена ничего не говорят. Их можно использовать только в том случае, если из контекста кода очевидно, какие данные хранит переменная.
- Договоритесь с вашей командой об используемых терминах. Если посетитель сайта называется «user», тогда мы должны называть связанные с ним переменные или , а не, к примеру, или .
Звучит просто? Действительно, это так, но на практике для создания описательных и кратких имён переменных зачастую требуется подумать. Действуйте.
Повторно использовать или создавать новую переменную?
И последняя заметка. Есть ленивые программисты, которые вместо объявления новых переменных повторно используют существующие.
В результате их переменные похожи на коробки, в которые люди бросают разные предметы, не меняя на них этикетки. Что сейчас находится внутри коробки? Кто знает? Нам необходимо подойти поближе и проверить.
Такие программисты немного экономят на объявлении переменных, но теряют в десять раз больше при отладке.
Дополнительная переменная – это добро, а не зло.
Современные JavaScript-минификаторы и браузеры оптимизируют код достаточно хорошо, поэтому он не создаёт проблем с производительностью. Использование разных переменных для разных значений может даже помочь движку оптимизировать ваш код.
Function Expressions
A JavaScript function can also be defined using an expression.
A function expression can be stored in a variable:
Example
var x = function (a, b) {return a * b};
After a function expression has been stored in a variable, the variable can
be used as a function:
Example
var x = function (a, b) {return a * b};
var z = x(4, 3);
The function above is actually an anonymous function (a function without a
name).
Functions stored in variables do not need function names. They are always
invoked (called) using the variable name.
The function above ends with a semicolon because it is a part of an executable statement.
Arrow Functions
Arrow functions allows a short syntax for writing function expressions.
You don’t need the keyword, the keyword, and the
curly brackets.
// ES5
var x = function(x, y) {
return x * y;
}
// ES6
const x = (x, y) => x * y;
Arrow functions do not have their own .
They are not well suited for defining object methods.
Arrow functions are not hoisted. They must be defined before they are used.
Using
is safer than using , because a function expression is
always constant value.
You can only omit the keyword and the curly brackets if the function is a single statement.
Because of this, it might be a good habit to always keep them:
Литералы и свойства
При использовании литерального синтаксиса мы сразу можем поместить в объект несколько свойств в виде пар «ключ: значение»:
У каждого свойства есть ключ (также называемый «имя» или «идентификатор»). После имени свойства следует двоеточие , и затем указывается значение свойства. Если в объекте несколько свойств, то они перечисляются через запятую.
В объекте сейчас находятся два свойства:
- Первое свойство с именем и значением .
- Второе свойство с именем и значением .
Можно сказать, что наш объект – это ящик с двумя папками, подписанными «name» и «age».
Мы можем в любой момент добавить в него новые папки, удалить папки или прочитать содержимое любой папки.
Для обращения к свойствам используется запись «через точку»:
Значение может быть любого типа. Давайте добавим свойство с логическим значением:
Для удаления свойства мы можем использовать оператор :
Имя свойства может состоять из нескольких слов, но тогда оно должно быть заключено в кавычки:
Последнее свойство объекта может заканчиваться запятой:
Это называется «висячая запятая». Такой подход упрощает добавление, удаление и перемещение свойств, так как все строки объекта становятся одинаковыми.
Объект, объявленный как константа, может быть изменён
Объект, объявленный через , может быть изменён.
Например:
Может показаться, что строка должна вызвать ошибку, но нет, здесь всё в порядке. Дело в том, что объявление защищает от изменений только саму переменную , а не её содержимое.
Определение выдаст ошибку только если мы присвоим переменной другое значение: .
Есть ещё один способ сделать константами свойства объекта, который мы рассмотрим в главе Флаги и дескрипторы свойств.
Синтаксис
Синтаксис для объявления функции:
Функция создаётся с заданными аргументами и телом .
Это проще понять на конкретном примере. Здесь объявлена функция с двумя аргументами:
А вот функция без аргументов, в этом случае достаточно указать только тело:
Главное отличие от других способов объявления функции, которые были рассмотрены ранее, заключается в том, что функция создаётся полностью «на лету» из строки, переданной во время выполнения.
Все предыдущие объявления требовали от нас, программистов, писать объявление функции в скрипте.
Но позволяет превратить любую строку в функцию. Например, можно получить новую функцию с сервера и затем выполнить её:
Это используется в очень специфических случаях, например, когда мы получаем код с сервера для динамической компиляции функции из шаблона, в сложных веб-приложениях.
Параметры, которые можно вызвать функцией
При вызове function ей можно отправить данные для обработки. Например, нижеследующий код выведет 2 сообщения:
function showMessage(from, text) { // параметры from, text from = "** " + from + " **"; // здесь вставляем сложный код оформления alert(from + ': ' + text); } showMessage('Петя', 'Привет!'); showMessage('Петя', 'Как дела?');
Помните, что когда код будет передан, параметры скопируются в локальные переменные. Также функцию можно вызвать практически с любым количеством аргументов, а если при вызове параметр не будет передан, он считается равным undefined. К примеру, функцию отображения сообщений showMessage(from, text) можно без проблем вызвать, используя один аргумент:
showMessage("Петя");
Вызов функции как метода
В JavaScript можно определить функции как методы объектов.
В следующем примере создается объект (myObject) с двумя свойствами (firstName и
lastName) и метод (fullName):
Пример
var myObject = {
firstName:»Андрей»,
lastName: «Щипунов»,
fullName: function () {
return this.firstName + » » + this.lastName;
}
}
myObject.fullName(); // Будет возвращать «Андрей Щипунов»
Метод fullName — это функция. Функция принадлежит объекту myObject является владельцем функции.
То, что называется , является объектом, который «владеет» кодом JavaScript.
В данном случае значение является myObject.
Проверьте! Измените метод fullName, чтобы вернуть значение :
Пример
var myObject = { firstName:»Андрей»,
lastName: «Щипунов»,
fullName: function () {
return this;
}}
myObject.fullName(); // Вернет (владельц object)
Вызов функции как метода объекта приводит к тому, что значением метода является сам объект.
Неотложные функции
Такие функции запускаются сразу, как обработчик дойдёт до них. То есть, не ждут вызова. Создаются простым добавлением скобок до и после выражения. Могут быть анонимными — не иметь названия.
Есть два наиболее распространенных синтаксиса:
(function optional_function_name() { //body }());
(function optional_function_name() { //body })();
Могут всречаться и другие способы:
// Другие способы !function() { /* ... */ }(); +function() { /* ... */ }(); new function() { /* ... */ };
Неотложные функции идеально подходят для написания кода, который должен выполниться только один раз: пространство имен, создание замыканий, создание частных переменных и многое другое. Для примера, представьте что с помощью JavaScript требуется получить язык страницы, а сделать это можно только проделав какие-то вычисления (например, ajax запрос).
var page_language = (function () { var lang; // Код для получения языка страницы return lang; })();
9) onReady()
Для инициализации страницы исторически использовалось событие window.onload, которое срабатывает после полной загрузки страницы и всех объектов на ней: счетчиков, картинок и т.п.
Событие — гораздо лучший выбор в 99% случаев. Это событие срабатывает, как только готов DOM документ, до загрузки картинок и других не влияющих на структуру документа объектов.
Это очень удобно, т.к. картинки могут загружаться долго, а обработчик может произвести необходимые изменения на странице и инициализацию интерфейсов тут же, не дожидаясь загрузки всего.
Для добавления обработчика можно использовать следующий кроссбраузерный код:
function bindReady(handler){ var called = false function ready() { // (1) if (called) return called = true handler() } if ( document.addEventListener ) { // (2) document.addEventListener( "DOMContentLoaded", function(){ ready() }, false ) } else if ( document.attachEvent ) { // (3) // (3.1) if ( document.documentElement.doScroll && window == window.top ) { function tryScroll(){ if (called) return if (!document.body) return try { document.documentElement.doScroll("left") ready() } catch(e) { setTimeout(tryScroll, 0) } } tryScroll() } // (3.2) document.attachEvent("onreadystatechange", function(){ if ( document.readyState === "complete" ) { ready() } }) } // (4) if (window.addEventListener) window.addEventListener('load', ready, false) else if (window.attachEvent) window.attachEvent('onload', ready) /* else // (4.1) window.onload=ready */ }
readyList = [] function onReady(handler) { if (!readyList.length) { bindReady(function() { for(var i=0; i<readyList.length; i++) { readyList() } }) } readyList.push(handler) }
Использование:
onReady(function() { // ... })
Подробное описание функций , и принципы их работы вы можете почерпнуть в статье Кроссбраузерное событие onDOMContentLoaded.
Возвращаемые значения
Функции могут не только принимать входящие данные из внешней программы, но и способны передавать обратно значения по завершении своих инструкций. Для возврата значения используется инструкция return.
Инструкция return имеет следующий синтаксис:
В программу, вызвавшую функцию, возвращается не само выражение, а результат его вычисления. Например, функция возвращает сумму двух чисел:
Выполнить код »
Скрыть результаты
Для дальнейшего использования возвращаемого значения, результат выполнения функции здесь присвоен переменной .
Инструкция может быть расположена в любом месте функции. После инструкции происходит выход из функции. Все операторы, расположенные в коде после инструкции , не будут выполнены, например:
Выполнить код »
Скрыть результаты
В инструкции можно указать сразу выражение, перепишем нашу
функцию :
Выполнить код »
Скрыть результаты
Внутри функции можно использовать несколько инструкций :
Выполнить код »
Скрыть результаты
В случае, если в функции нет инструкции или указана без аргументов, то функция вернёт значение :
Выполнить код »
Скрыть результаты
Усиливаем пример с определением имени. Тесты
const myFunc = function() { }; // 1 const myFuncA = function myFuncA2() { }; // 2const myFuncB = () => { }; // 3const myFuncC = new Function(); // 4const property = Symbol('symbolProperty');const myObject = { methodA: function() { }, // 5 methodB: function MyMethodB() {}, // 6 methodC: () => { }, // 7 methodD(){ }, // 8 (){ } // 9};function myFuncD() { }; // 10(function() { })(); // 11(function myFuncF(){ })(); // 12
Теперь, используя описанные способы выше, возьмём у каждой функции свойство name, и начнем с функций, заданных как :
myFunc.name // "myFunc"myFuncA.name // "myFuncA"myFuncB.name // "myFuncB"myFuncC.name // "myFuncC"
Функции, объявленные как Object Method:
myObject.methodA.name // "methodA"myObject.methodB.name // "MyMethodB"myObject.methodC.name // "methodC"myObject.methodD.name // "methodD"myObject.name // ""
Функции, объявленные как IIFE (Immediately-invoked function expression):
(function myFuncD() { }).name // "myFuncD"(function() { }).name // ""(function myFuncF(){ }).name // "myFuncF"(new Function()).name // "anonymous"
Вопросов возникает ещё больше. А может функция в опросе из twitter все таки именованная? Может я ввел вас в заблуждение?
Вложенные функции
Функция называется «вложенной», когда она создаётся внутри другой функции.
Это очень легко сделать в JavaScript.
Мы можем использовать это для упорядочивания нашего кода, например, как здесь:
Здесь вложенная функция создана для удобства. Она может получить доступ к внешним переменным и, значит, вывести полное имя. В JavaScript вложенные функции используются очень часто.
Что ещё интереснее, вложенная функция может быть возвращена: либо в качестве свойства нового объекта (если внешняя функция создаёт объект с методами), либо сама по себе. И затем может быть использована в любом месте
Не важно где, она всё так же будет иметь доступ к тем же внешним переменным
Например, здесь, вложенная функция присваивается новому объекту в конструкторе:
А здесь мы просто создаём и возвращаем функцию «счётчик»:
Давайте продолжим с примером . Он создаёт функцию «counter», которая возвращает следующее число при каждом вызове. Несмотря на простоту, немного модифицированные варианты этого кода применяются на практике, например, в генераторе псевдослучайных чисел и во многих других случаях.
Как же это работает изнутри?
Когда внутренняя функция начинает выполняться, начинается поиск переменной изнутри-наружу. Для примера выше порядок будет такой:
- Локальные переменные вложенной функции…
- Переменные внешней функции…
- И так далее, пока не будут достигнуты глобальные переменные.
В этом примере будет найден на шаге . Когда внешняя переменная модифицируется, она изменится там, где была найдена. Значит, найдёт внешнюю переменную и увеличит её значение в лексическом окружении, которому она принадлежит. Как если бы у нас было .
Теперь рассмотрим два вопроса:
- Можем ли мы каким-нибудь образом сбросить счётчик из кода, который не принадлежит ? Например, после вызова в коде выше.
- Если мы вызываем несколько раз – нам возвращается много функций . Они независимы или разделяют одну и ту же переменную ?
Попробуйте ответить на эти вопросы перед тем, как продолжить чтение.
…
Готовы?
Хорошо, давайте ответим на вопросы.
- Такой возможности нет: – локальная переменная функции, мы не можем получить к ней доступ извне.
- Для каждого вызова создаётся новое лексическое окружение функции, со своим собственным . Так что, получившиеся функции – независимы.
Вот демо:
Надеюсь, ситуация с внешними переменными теперь ясна. Для большинства ситуаций такого понимания вполне достаточно, но в спецификации есть ряд деталей, которые мы, для простоты, опустили. Далее мы разберём происходящее ещё более подробно.
Переходим к нескольким аргументам с «func.apply»
Теперь давайте сделаем ещё более универсальным. До сих пор он работал только с функциями с одним аргументом.
Как же кешировать метод с несколькими аргументами ?
Здесь у нас есть две задачи для решения.
Во-первых, как использовать оба аргумента и для ключа в коллекции ? Ранее для одного аргумента мы могли просто сохранить результат и вызвать , чтобы получить его позже. Но теперь нам нужно запомнить результат для комбинации аргументов . Встроенный принимает только одно значение как ключ.
Есть много возможных решений:
- Реализовать новую (или использовать стороннюю) структуру данных для коллекции, которая более универсальна, чем встроенный , и поддерживает множественные ключи.
- Использовать вложенные коллекции: будет , которая хранит пару . Тогда получить мы сможем, вызвав .
- Соединить два значения в одно. В нашем конкретном случае мы можем просто использовать строку как ключ к . Для гибкости, мы можем позволить передавать хеширующую функцию в декоратор, которая знает, как сделать одно значение из многих.
Для многих практических применений третий вариант достаточно хорош, поэтому мы будем придерживаться его.
Также нам понадобится заменить на , чтобы передавать все аргументы обёрнутой функции, а не только первый.
Вот более мощный :
Теперь он работает с любым количеством аргументов.
Есть два изменения:
- В строке вызываем для создания одного ключа из . Здесь мы используем простую функцию «объединения», которая превращает аргументы в ключ . В более сложных случаях могут потребоваться другие функции хеширования.
- Затем в строке используем для передачи как контекста, так и всех аргументов, полученных обёрткой (независимо от их количества), в исходную функцию.
Вместо мы могли бы написать .
Синтаксис встроенного метода func.apply:
Он выполняет , устанавливая и принимая в качестве списка аргументов псевдомассив .
Единственная разница в синтаксисе между и состоит в том, что ожидает список аргументов, в то время как принимает псевдомассив.
Эти два вызова почти эквивалентны:
Есть только одна небольшая разница:
- Оператор расширения позволяет передавать перебираемый объект в виде списка в .
- А принимает только псевдомассив .
Так что эти вызовы дополняют друг друга. Для перебираемых объектов сработает , а где мы ожидаем псевдомассив – .
А если у нас объект, который и то, и другое, например, реальный массив, то технически мы могли бы использовать любой метод, но , вероятно, будет быстрее, потому что большинство движков JavaScript внутренне оптимизируют его лучше.
Передача всех аргументов вместе с контекстом другой функции называется «перенаправлением вызова» (call forwarding).
Простейший вид такого перенаправления:
При вызове из внешнего кода его не отличить от вызова исходной функции.
«this» не является фиксированным
В JavaScript ключевое слово «this» ведёт себя иначе, чем в большинстве других языков программирования. Оно может использоваться в любой функции.
В этом коде нет синтаксической ошибки:
Значение вычисляется во время выполнения кода и зависит от контекста.
Например, здесь одна и та же функция назначена двум разным объектам и имеет различное значение «this» при вызовах:
Правило простое: при вызове значение внутри равно . Так что, в приведённом примере это или .
Вызов без объекта:
Мы даже можем вызвать функцию вовсе без использования объекта:
В строгом режиме () в таком коде значением будет являться . Если мы попытаемся получить доступ к , используя – это вызовет ошибку.
В нестрогом режиме значением в таком случае будет глобальный объект ( для браузера, мы вернёмся к этому позже в главе Глобальный объект). Это – исторически сложившееся поведение , которое исправляется использованием строгого режима ().
Обычно подобный вызов является ошибкой программирования. Если внутри функции используется , тогда ожидается, что она будет вызываться в контексте какого-либо объекта.
Последствия свободного
Если вы до этого изучали другие языки программирования, тогда вы, скорее всего, привыкли к идее «фиксированного » – когда методы, определённые внутри объекта, всегда сохраняют в качестве значения ссылку на свой объект (в котором был определён метод).
В JavaScript является «свободным», его значение вычисляется в момент вызова метода и не зависит от того, где этот метод был объявлен, а зависит от того, какой объект вызывает метод (какой объект стоит «перед точкой»).
Эта идея вычисления в момент исполнения имеет как свои плюсы, так и минусы. С одной стороны, функция может быть повторно использована в качестве метода у различных объектов (что повышает гибкость). С другой стороны, большая гибкость увеличивает вероятность ошибок.
Здесь мы не будем судить о том, является ли это решение в языке хорошим или плохим. Мы должны понимать, как с этим работать, чтобы получать выгоды и избегать проблем.
Вызов функции с помощью конструктора функций
Если вызову функции предшествует ключевое слово , то это вызов конструктора.
Похоже, что вы создаете новую функцию, но поскольку функции JavaScript являются объектами, вы фактически создаете новый объект:
Пример
// Это конструктор функций:function myFunction(arg1, arg2) {
this.firstName = arg1;
this.lastName = arg2;
}
// This создает новый объект
var x = new myFunction(«Андрей», «Щипунов»);
x.firstName; // Будет возвращать «Андрей»
Вызов конструктора создает новый объект. Новый объект наследует свойства и методы от своего конструктора.
Ключевое слово в конструкторе не имеет значения.
Значением параметра будет новый объект, созданный при вызове функции.
Итого
Основные улучшения в функциях:
- Можно задавать параметры по умолчанию, а также использовать деструктуризацию для чтения приходящего объекта.
- Оператор spread (троеточие) в объявлении позволяет функции получать оставшиеся аргументы в массив: .
- Тот же оператор spread в вызове функции позволяет передать в неё массив как список аргументов (вместо ).
- У функции есть свойство , оно содержит имя, указанное при объявлении функции, либо, если его нет, то имя свойства или переменную, в которую она записана. Есть и некоторые другие ситуации, в которых интерпретатор подставляет «самое подходящее» имя.
- Объявление Function Declaration в блоке видно только в этом блоке.
- Появились функции-стрелки:
- Без фигурных скобок возвращают выражение : .
- С фигурными скобками требуют явного .
- Не имеют своих и , при обращении получают их из окружающего контекста.
- Не могут быть использованы как конструкторы, с .