Javascript полезные функции часть v


Содержание

Самовыполняющиеся функции [дубликат]

На данный вопрос уже ответили:

Зачем оборачивать блоки кода на JS в самовыполняющиеся функции?

Отмечен как дубликат участниками PashaPash, Grundy, Axifive, Igor, Yuri Glushenkov 1 дек ’15 в 19:42 .

Подобный вопрос задавали ранее и на него уже получен ответ. Если представленные ответы не являются исчерпывающими, пожалуйста, задайте новый вопрос.

1 ответ 1

Переменные, объявленные внутри функции, являются локальными (находятся в её области видимости). Это даёт как минимум 3 полезных момента:

  1. «Снаружи» не «достучаться» до того, что объявлено внутри функции.
  2. Не получится случайно затереть глобальную переменную, объявив переменную с таким же именем внутри функции.
  3. Глобальная область видимости не «захламляется» данными, которые не должны быть глобальными.

Самовыполняющаяся функция (Immediately-Invoked Function Expression, IIFE) позволяет воспользоваться этими преимуществами, не создавая глобальную функцию.

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

Переменная count недоступна в глобальной области видимости, однако доступна внутри возвращаемой функции, так как та объявлена внутри самовыполняющейся функции, и поэтому имеет доступ к её области видимости.

Также самовыполняющаяся функция может «помочь» с сохранением промежуточных значений данных при асинхронном выполнении кода.

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

Если же обернуть setTimeout в функцию и передавать ей i в качестве аргумента, то вывод будет от 0 до 9 :

В данном случае переменная i в setTimeout уже не является глобальной переменной и не зависит от её изменений. Однако тут не нужно забывать о том, что если переменная является объектом, то изменение значения поля объекта «снаружи» приведёт и к изменению значения поля объекта «внутри».

JavaScript функции. Часть 1

Дата публикации: 2020-10-26

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

С функциями мы с вами уже на самом деле встречались. Вспомните методы alert, prompt и confirm — это и есть функции языка JavaScript. Они уже готовы к использованию и мы можем их вызывать в нашем коде. Кроме того, JavaScript предлагает нам сотни других функций, со многими из которых вы познакомитесь при дальнейшем изучении JavaScript.

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

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

Давайте посмотрим, как это происходит на практике. Пока что просто учебный пример — функция, которая будет выводить модальное окно с сообщением:

Как создать сайт самому?

Какие технологии и знания необходимы сегодня, чтобы создавать сайты самостоятельно? Узнайте на интенсиве!

Самовызывающиеся функции в Javascript

| Суббота, 26 июля, 2014

Javascript кажется немного странным языком, который иногда делает странные вещи. Такие мысли могут возникнуть, если не следить за лучшими практиками и не быть знакомым со стандартом ECMA. Странности встречаются и в синтаксисе и семантике. Одна из таких вещей – это самовызывающиеся функции (self-invoking functions).

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

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

Что фактически будет происходить?

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

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

Существует несколько видов синтаксиса этих функций. Хотя разница очень мала, но приведем примеры. Дуглас Крокфорд, один из разработчиков Javascript, считает правильной такую запись самовызывающейся функции:

А вот другой вариант, который Крокфорд, называет «собачьи яйца» (dog balls).

Некоторые считают, что второй вариант более удобен.

Такой вариант тоже работает, но почему-то нигде не встречается:

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

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

И самое интересное, ради чего и замыслился весь этот пост, самовызывающуюся функцию можно использовать в модульном паттерне (module pattern). Мы сохраняем ссылку на возвращаемое значение, а именно на API модуля:

Статей про модульный паттерн существует очень много, очень удобная штука для тех, кто писал классы в C#, С++ т.п.

Итак, самовызывающиеся функции – это функции, вызывающие сами себя, использовать их или нет зависит от вас, но понимать их надо. Иногда их еще называют как немедленно исполняемые функции (Immediately Invoked Functions).

JavaScript функции

Функции являются одним из наиболее важных строительных блоков кода в JavaScript.

Функции состоят из набора команд и обычно выполняют какую-то одну определенную задачу (например суммирование чисел, вычисление корня и т.д.).

Код помещенный в функцию будет выполнен только после явного вызова этой функции.

Объявление функций

имяфункции задает имя функции. Каждая функция на странице должна иметь уникальное имя. Имя функции должно быть задано латинскими буквами и не должно начинаться с цифр.

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

Обратите внимание: даже если в функцию не передаются переменные не забывайте вставлять круглые скобки «()» после имени функции.

Обратите внимание: имена функций в JavaScript чувствительны к регистру.

Пример JavaScript функции

Функция messageWrite() в примере ниже будет выполнена только после нажатия на кнопку.

Обратите внимание: в этом примере используется событие onclick. События JavaScript будут подробно рассмотрены далее в данном учебнике.

Передача функциям переменных

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

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

Чтобы обращаться к глобальной переменной из функции, а не ее копии используйте window.имя_переменной.

Команда return

С помощью команды return Вы можете возвращать из функций значения.

Встроенные функции

Помимо определяемых пользователем функций в JavaScript существуют еще и встроенные функции.

К примеру встроенная функция isFinite позволяет проверить является ли переданное значение допустимым числом.

Обратите внимание: полный список встроенных функций JavaScript Вы можете найти в нашем JavaScript Справочнике.

Локальные и глобальные переменные

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

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

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

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

Глобальные переменные уничтожаются только после закрытия страницы.

Обратите внимание: при выводе на экран переменная var2 будет иметь пустое значение, так как func1 оперирует с локальной «версией» переменной var2.

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

Функции, которые не содержат имени при объявлении называются анонимными.

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

Функции в JavaScript

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

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

Замечательно то, что функция НЕ выполнится, если не будет вызвана.

Записываются функции в общем виде следующем виде:

К именам функций существуют те же требования, что и к именам переменных. Важно помнить, что функция — это особый вид переменных в JavaScript, поэтому задавать одинаковые имена для функции и для переменной НЕЛЬЗЯ.

Например, нам нужна функция, которая печатает в тело документа сегодняшнее число.

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

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

Вызов функции без аргументов

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

Что можно записывать в функциях JavaScript

Функции в своем коде могут иметь различные конструкции, характерные для языка JavaScript:

  • объявление переменных ( var, let, const );
  • арифметические операции (сложение, вычитание и т.д.);
  • условные конструкции — if. else , switch. case , тернарный оператор;
  • любые виды циклов — for() , while() , do. while() , for. in ;
  • вызов других функций.

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

table += »

Ячейка » + ( j + 1 ) + «

» ;

А следующая функция будет проверять четность/нечетность числа, введенного пользователем:

Проверить четность числа

В функции использована условная конструкция if. else и тернарный оператор.

Оператор return

Оператор return позволяет вернуть значение из функции, т.е. получить результат для дальнейшего использования в коде. Этот результат можно записать (присвоить) в какую-либо переменную, использовать в дальнейшем коде для вычислений, например, или вывести на html-страницу.

С помощью return можно возвращать логические значения ( true или false ), результат вычислений (число), строку или даже объект. Следует понимать 2 особенности, связанные с этим оператором:

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

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

Посчитать сумму чисел

Рассмотрим пример посложнее. Нам необходимо создать объект на html-странице, причем не один, а несколько (в примере будет 2 — div и абзац). Выполняться это будет в функции. Затем в основном коде для каждого из элементов задается класс, описанный в стилях.

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

Примечание: данный скрипт должен быть размещен в body, и перед тегом Посчитать сумму строк

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

Посчитать сумму чисел с проверкой

Здесь оператор «+» преобразует строку в число, если строка вида «12», «-14» и т.д. В случае строки «abcd12» происходит преобразование в тип NaN (Not a Number) и вычисление суммы не выполняется. Выводится сообщение об ошибке и возвращается 0 (ноль). Обратите внимание, что после оператора return выполнение функции прекращается.

Кстати, функция, которая не имеет оператора return, на самом деле возвращает значение undefined.

Еще немного про аргументы функций в Javascript

В Javascript существует псевдомассив аргументов функции, который так и называется — arguments. И, если точное количество аргументов неизвестно, то можно воспользоваться этим массивом. Он имеет свойство length, как и обычные массивы, но методы массивов push() , pop() и т.д. вы к нему применить не сможете.

Рассмотрим использование этого массива на примере функции суммы:

Сумма чисел » + summa ( 2 , 3 , 56 , 17 , 34 , 67 ) + «

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

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

table += »

Ячейка » + ( j + 1 ) + «

» ;

В первой строчке сценария функции мы проверяем, а существует ли значение для переменной row, и если она равна undefined, т.е. значения нет, то присваиваем ей значение 2. Во второй строке можно присвоить переменной cols одно из значений — либо переданное в функцию, либо, если параметр не был передан, т.е. он равен undefined, мы назначаем опять-таки значение 2.

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

table += »

Ячейка » + ( j + 1 ) + «

» ;

Типы функций в Javascript

Все функции, которые мы рассматривали до сих пор, относятся к категории Function Declaration, т.е. функции объявленной. Кроме того, существует тип функций Function Expression, т.е. функции-выражения. Посмотрите на разницу между их объявлением:

Function Expression представляет собой переменную, в которую записывается код функции. Чаще всего такой вариант применяют для использования функции в качестве метода объекта.

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

Declaration vs Expression

Для того чтобы увидеть ошибку, нужно открыть консоль браузера (F12 и Esc).

Так вот в этом простом примере мы не увидели второго окна alert() , т.к. функция func_expression() была вызвана ДО ее объявления. И это вызвало ошибку интерпретатора, т.к. Function expression НЕ вычисляется до выполнения javascript, из-за того, что является операцией присваивания переменной. Поэтому вызов function expression нельзя осуществлять до объявления самой функции, так как переменная на тот момент содержит undefined . Именно поэтому хорошим стилем кода на JavaScript является объявление всех функций в верхней части скрипта.

Что же касается Function declaration, то Javascript выявляет все определения таких функций и переносит их в начало сценария. Т.е. если в коде присутствуют такие функции, то javascript знает о них еще до выполнения сценария. Именно поэтому не важно, в каком месте кода вызывается Function declaration. Но все-таки, если есть возможность, объявляйте функции в начале сценария.

Области видимости переменных

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

В примере ниже глобальная переменная a перезаписывается внутри функции func() , а затем и во внутренней функции innerFunc() . А это далеко не всегда приводит к нужным результатам в процессе выполнения кода.

Функция Javascript, которая выполняется сразу после ее определения (немедленно вызываемые функции).

При разработке скриптов на языке Javascript, иногда удобно запускать функцию в момент ее определения.

Наверняка, вы привыкли работать вот с таким описанием функции:

Но, есть и другой способ работы с функциями:

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

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

() – эти скобки являются вызовом функции. Мы просто создаем анонимную функцию и тут же ее запускаем.

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

Такие функции называются немедленно вызываемыми функциями или immediately invoked function (IIFE). Как и обычным функциям, им тоже можно передавать параметры:

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

То же самое можно написать и так:

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

Теперь, когда вы будете разбираться с очередным скриптом на Javascript, вы будете знать, что означает такая форма записи.

Кстати, если хотите узнать конверсии и ключевые показатели (KPI) для вашего продающего сайта, могу настроить веб-аналитику.

Яндекс Метрика и Google Analytics. Цели, события, отчеты.

Подробно о функциях JavaScript

Функции JavaScript определяются при помощи ключевого слова function.

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

Декларация функции

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

Декларированные функции выполняются не сразу. Они имеют статус «отложенного использования» и выполняются только тогда, когда к ним обратятся (вызовут).

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

Функции-выражения

Также функции JavaScript могут определяться с использованием выражения.

Функции-выражения могут храниться в переменных:

После того как функция-выражение сохранена в переменной, эта переменная может использоваться как функция:

На самом деле функция в предыдущем примере является анонимной (функция без имени).

Функциям, сохраненным в переменных, не нужны имена. Они вызываются при помощи имени переменной.

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

Конструктор Function()

Как вы видели в предыдущих примерах, функции JavaScript определяются при помощи ключевого слова function.

Однако, кроме этого функции также могут определяться при помощи встроенной функции конструктора Function().

Тем не менее, на самом деле вам не нужно использовать функцию конструктора.

Предыдущий пример аналогичен следующему коду:

В большинстве случаев в JavaScript можно избежать использование ключевого слова new.

Поднятие функций

Ранее в этом учебнике вы уже познакомились с таким понятием, как «поднятие» переменных. (Глава Поднятие переменных в Javascript)

«Поднятие» — это такое поведение JavaScript, при котором декларации перемещаются (поднимаются) в начало текущей области видимости.

Поднятие применяется к декларациям переменных и функций.

Благодаря этому функции JavaScript можно вызывать до того, как они будут декларированы:

Внимание! Функции, декларированные как выражения, не поднимаются.

Самовызываемые функции

Функции-выражения могут быть «самовызываемыми».

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

Функция-выражение будет выполняться автоматически, если после выражения стоят круглые скобки ().

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

Функция в предыдущем примере это анонимная самовызываемая функция.

Функции могут использоваться как значения

Функции JavaScript могут использоваться как значения:

Функции JavaScript могут использоваться в выражениях:

Функции — это объекты

Оператор typeof в JavaScript возвращает для функций тип «function». Однако лучше всего функции JavaScript можно описать как объекты.

У функций JavaScript есть свойства и методы.

Свойство arguments.length возвращает количество параметров, полученных при вызове функции:

Метод toString() возвращает функцию как строку:

Функция, определенная как свойство объекта, называется методом объекта.

Функция, предназначенная для создания новых объектов, называется конструктором объекта.

«Стрелочные» функции

«Стрелочные» функции предоставляют короткую запись для выражений-функций.

При этом в выражении-функции не нужно использовать ключевые слова function, return и фигурные скобки:

У стрелочных функций нет своего значения this. И они не подходят для определения методов объектов.

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

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

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

Хорошей привычкой будет писать их всегда:

Стрелочные функции не поддерживаются в IE11 и более ранних версиях IE.

10 ошибок, которые часто допускают новички JavaScript

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

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

1. Пропуск фигурных скобок

Одна из ошибок, которую часто допускают новички JavaScript — пропуск фигурных скобок после операторов типа if, else,while и for . Хотя это не запрещается, вы должны быть очень осторожны, потому что это может стать причиной скрытой проблемы и позже привести к ошибке.

Смотрите пример, приведенный ниже:

Хотя вызов fail() имеет отступ и, кажется, будто он принадлежит оператору if , это не так. Он вызывается всегда. Так что это полезная практика окружать все блоки кода фигурными скобками, даже если в них присутствует только один оператор.

2. Отсутствие точек с запятой

Во время парсировки JavaScript осуществляется процесс, известный как автоматическая расстановка точек с запятой. Как следует из названия, анализатор расставляет недостающие знаки вместо вас.

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

Так как в строке 3 отсутствует точка с запятой, анализатор предполагает, что открывающаяся скобка в строке 5 является попыткой доступа к свойству, используя синтаксис массива аксессора (смотри ошибку № 8), а не отдельным массивом, который является не тем, что предполагалось.

Это приводит к ошибке. Исправить это просто — всегда вставляйте точку с запятой.

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

3. Непонимание приведений типа

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

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

Проблема может быть легко исправлена с применением parseInt(textBox.value, 10) , чтобы перевести строку в число перед добавлением к ней 10.

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

Чтобы не допустить преобразования типа при сравнении переменных в операторе if , вы можете использовать проверку строгого равенства ( === ).

4. Забытые var

Еще одна ошибка, допускаемая новичками — они забывают использовать ключевое слово var при объявлении переменных. JavaScript — очень либеральный движок.

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

Вот пример, который кроме того иллюстрирует и другую ошибку — недостающую запятую при объявлении сразу нескольких переменных:

Когда анализатор достигает строки 4, он автоматически добавит точку с запятой, а затем интерпретирует объявления c и d в строке 5, как глобальные.

Это приведет к изменению значения другой переменной c. Больше о подводных камнях JavaScript здесь .

5. Арифметические операции с плавающей точкой

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

Чтобы обойти эту проблему, вы не должны использовать десятичные числа, если вам нужна абсолютная точность — используйте целые числа, или если вам нужно работать с денежными единицами, используйте библиотеку типа bignumber.js .

6. Использование конструкторов вместо оригинальных обозначений

Когда программисты Java и C # начинают писать на JavaScript, они часто предпочитают создавать объекты с использованием конструкторов: new Array(), new Object(), new String() .

Хотя они прекрасно поддерживаются, рекомендуется использовать оригинальные обозначения: [], <>, «» , так как конструкторы имеют свои особенности:

Решение этой проблемы просто: попробуйте всегда использовать буквальные оригинальные
обозначения. Кроме того, в JS не обязательно указывать размер массивов заранее.

7. Непонимание того, как разделяются диапазоны

Одна из трудных для понимания новичками вещей в JS , это правила разграничения и закрытия диапазонов. И это действительно не просто:

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

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

8. Использование Eval

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

Код внутри eval — это строка. Отладочные сообщения, связанные с Eval-блоками непонятны, и вам придется поломать голову, чтобы правильно расставить одинарные и двойные кавычки.

Не говоря уже о том, что это будет работать медленнее, чем обычный JavaScript . Не используйте Eval если вы не знаете точно, что вы делаете.

9. Непонимание асинхронного кода

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

Новичкам еще трудно понимать это интуитивно, и они быстро заходят в тупик, сталкиваясь с ошибками, которые трудно понять.

Вот пример, в котором я использую сервис FreeGeoIP для определения вашего местоположения по IP-адресу:

Несмотря на то, что console.log располагается после вызова функции load() , на самом деле он выполняется перед определением данных.

10. Злоупотребление отслеживанием событий

Давайте предположим, что вы хотите отслеживать клик кнопки, но только при условии установленного чеккера.

Новичок в этом случае может сделать следующее (используя JQuery ):

Что-то, очевидно, не так. В идеале, вы должны отслеживать события только один раз, как задано для изменения события чекбокса.

Повторяющийся вызов button.on(‘click’ ..) приводит к повторяющемуся отслеживанию события, которое никогда не удаляется.

Я оставлю данный пример без изменений в качестве упражнения для читателей, чтобы они могли поработать над ним ��

Заключение

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

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

Данная публикация представляет собой перевод статьи « 10 Mistakes That JavaScript Beginners Often Make » , подготовленной дружной командой проекта Интернет-технологии.ру

JavaScript функции

Функции являются одним из наиболее важных строительных блоков кода в JavaScript.

Функции состоят из набора команд и обычно выполняют какую-то одну определенную задачу (например суммирование чисел, вычисление корня и т.д.).

Код помещенный в функцию будет выполнен только после явного вызова этой функции.

Объявление функций

имяфункции задает имя функции. Каждая функция на странице должна иметь уникальное имя. Имя функции должно быть задано латинскими буквами и не должно начинаться с цифр.

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

Обратите внимание: даже если в функцию не передаются переменные не забывайте вставлять круглые скобки «()» после имени функции.

Обратите внимание: имена функций в JavaScript чувствительны к регистру.

Пример JavaScript функции

Функция messageWrite() в примере ниже будет выполнена только после нажатия на кнопку.

Обратите внимание: в этом примере используется событие onclick. События JavaScript будут подробно рассмотрены далее в данном учебнике.

Передача функциям переменных

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

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

Чтобы обращаться к глобальной переменной из функции, а не ее копии используйте window.имя_переменной.

Команда return

С помощью команды return Вы можете возвращать из функций значения.

Встроенные функции

Помимо определяемых пользователем функций в JavaScript существуют еще и встроенные функции.

К примеру встроенная функция isFinite позволяет проверить является ли переданное значение допустимым числом.

Обратите внимание: полный список встроенных функций JavaScript Вы можете найти в нашем JavaScript Справочнике.

Локальные и глобальные переменные

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

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

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

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

Глобальные переменные уничтожаются только после закрытия страницы.

Обратите внимание: при выводе на экран переменная var2 будет иметь пустое значение, так как func1 оперирует с локальной «версией» переменной var2.

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

Функции, которые не содержат имени при объявлении называются анонимными.

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

Как работает JavaScript

Почему понимание основ бесценно

Вы, наверное, удивляетесь, почему кто-то потрудится написать длинный пост о том как работает ядро JavaScript в 2020 году.

Это потому, что я считаю, что в наши дни очень легко потеряться в экосистеме JS без глубокого понимания основ и практически невозможно исследовать более сложные темы.

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

Как это работает?

Компьютеры не понимают JavaScript – браузеры понимают.

Помимо обработки сетевых запросов, прослушивания кликов мыши и интерпретации HTML и CSS для рисования пикселей на экране, в браузер встроен движок JavaScript.

Движок JavaScript – это программа, написанная, скажем, на C ++, которая обрабатывает весь код JavaScript, символ за символом, и «превращает» его в нечто, что ЦП компьютера может понять и выполнить – то есть в машинный код.

Это происходит синхронно, то есть по одной строке за раз и по порядку.

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

Таким образом, движок делают всю эту работу за разработчиков JavaScript, иначе веб-разработка была бы намного сложнее, менее популярной, и у нас не было бы таких вещей, как Medium (оригинальная статья размещена на Medium), где мы могли бы писать статьи, подобные этой (и я бы сейчас спал) ,

Движки JavaScript отличаются по типу выполнения. Он может пройти по каждой строке JavaScript и выполнять их по строчно (см. Интерпретатор), или он может быть умнее и обнаруживать такие вещи, как функции, которые часто вызываются и всегда дают один и тот же результат. Затем он может скомпилировать их в машинный код только один раз, чтобы в следующий раз, когда он встретит его, он выполнял уже скомпилированный код, что намного быстрее (см. Компиляция Just-in-time). Или он может заранее скомпилировать весь код в машинный код (см. Compiler).

Сейчас самый распространенный V8 – это такой движок JavaScript, который Google был создан в 2008 году. В 2009 году у парня по имени Райан Даль появилась идея использовать V8 для создания Node.js, среды выполнения для JavaScript вне браузера, что означало, что этот язык может также использоваться для серверных приложений.

Контекст выполнения функции

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

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

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

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

Каждый раз, когда вызывается функция, ей нужно все это понять. Это достигается путем создания оболочки, называемой контекстом выполнения (execution contex).

Чтобы быть более конкретным и избежать путаницы в будущем, я буду называть этот контекст выполнения функции (function execution context), потому что он создается каждый раз, когда вызывается функция. Не пугайтесь этого термина и не думайте о нем слишком много сейчас, он будет подробно описано позже.

Просто помните, что он определяет такие вещи, как: «Какие переменные доступны в этой конкретной функции, какое значение this внутри нее, какие переменные и функции объявлены внутри нее?»

Глобальный контекст выполнения

Но не весь код JavaScript находится внутри функций.

Также код может быть вне любой функции на глобальном уровне, поэтому одна из самых первых вещей, которую делает движок JavaScript, – это создание глобального контекста выполнения (global execution context).

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

Например, существует только один глобальный контекст выполнения, созданный в начале выполнения, внутри которого выполняется весь код JavaScript.

Глобальный контекст выполнения создает две вещи, которые специфичны для него, даже если нет кода для выполнения:

  • Объект window, когда JavaScript запускается внутри браузера. Когда он запускается вне его, как в случае с Node.js, он будет чем-то вроде global. Для простоты я буду использовать window в этой статье.
  • Специальную переменная которая называется this.

В глобальном контексте выполнения, и только там, this фактически равняется объекту window. Это в основном ссылка на window.

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

Хотя функции также имеют специальную переменную this, этого не происходит в контексте выполнения функции.

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

Все встроенные в JavaScript переменные и функции присоединяются к глобальному объекту window: setTimeout(), localStorage, scrollTo(), Math, fetch() и т. д. Именно поэтому они доступны в любом месте кода.

Стек исполнения (Execution Stack)

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

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

Посмотрите на следующий пример:

Когда происходит вызов функции a(), создается контекст выполнения функции, как описано выше, и выполняется код внутри функции.

Когда выполнение кода завершено (оператор return или скобка > ), контекст выполнения функции для функции a() уничтожается.

Затем происходит вызов b(), и тот же процесс повторяется для функции b().

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

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

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

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

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

Этот стек называется стеком выполнения (execution stack), представленным на рисунке ниже.

JavaScript execution stack

Когда стек выполнения пуст, выполнения глобальный контекст, который мы обсуждали ранее и который никогда не уничтожается, оставаясь текущим контекстом выполнения.

Очередь событий (Event Queue)

Помните, когда я говорил, что движок JavaScript – это всего лишь один компонент браузера, наряду с движком рендеринга или сетевым уровнем?

Эти компоненты имеют встроенные хуки (Hooks), с помощью которых ядро взаимодействует для инициирования сетевых запросов, рисования пикселей на экране или прослушивания щелчков мыши.

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

Но это может занять несколько секунд, что делает движок JavaScript во время выполнения запроса?

Просто прекращает выполнение любого кода, пока не придет ответ? Продолжает выполнение остальной части кода и, когда приходит ответ, останавливать все и выполнять обратный вызов? Или когда обратный вызов завершается, возобновляет выполнение с того места, где он остановился?

Ничего из вышеперечисленного, хотя первое может быть достигнуто с помощью await.

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

Чтобы понять, как это на самом деле работает, давайте рассмотрим функции a() и b(), которые мы рассматривали ранее, но добавим обработчик кликов мыши и обработчик HTTP-запросов.

Любое событие, полученное движком JavaScript от других компонентов браузера, например клик мыши или ответ по сети, не будет обработано немедленно.

Движок JavaScript может быть занят выполнением кода в этот момент, поэтому вместо этого он поместит событие в очередь, называемую очередью событий (event queue).

JavaScript Event Queue

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

Затем следующий контекст выполнения возобновляет выполнение до его завершения и т. д. До тех пор, пока стек не станет пустым, а глобальный контекст выполнения не станет текущим выполняемым контекстом выполнения.

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

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

Поскольку этот обработчик является функцией JavaScript, он будет обрабатываться так же, как обрабатывались a() и b(), что означает, что создается контекст выполнения функции и помещается в стек выполнения.

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

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

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

Вы можете легко увидеть это в действии, скопировав следующий код в консоль браузера:

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

Когда пройдет пять секунд, и стек выполнения станет пустым, вызовется обработчик первого клика.

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

На самом деле, то же самое происходит с setTimeout()setInterval()). Обработчик, который вы предоставите для setTimeout(), фактически помещается в очередь событий. Это означает, что если вы установите тайм-аут на 0, но в стеке выполнения будет код, обработчик для setTimeout() будет вызван только тогда, когда стек будет пустым, что может произойти через множество миллисекунд.

Это одна из причин, почему setTimeout() и setInterval() не совсем точны. Скопируйте и вставьте следующий текст в консоль браузера, если вы не поверите мне на слово.

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

Этапы контекста выполнения функции

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

Это происходит в два этапа: этап создания и этап выполнения.

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

На этапе создания происходят две вещи, которые очень важны:

  • определяется область действия (scope).
  • определяется значение this (я предполагаю, что вы уже знакомы с ключевым словом this в JavaScript).

Каждый из них подробно описан в следующих двух соответствующих разделах.

Scope и Scope Chain

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

При достижении console.log(foo), движок JavaScript сначала проверит, есть ли переменная foo в области контекста выполнения b(). Поскольку там ее нет, он перейдет в «родительский» контекст выполнения, который является контекстом выполнения a(), просто потому, что b() объявлен внутри a(). В области видимости этого контекста выполнения он найдет foo и выведет ее значение.

Если мы определим b() вне a(), вот так:

Будет сгенерировано ReferenceError, хотя единственное различие между ними заключается в месте, где объявлено b().

«Родительская» область действия b() теперь является глобальной областью контекста выполнения, поскольку она объявляется на глобальном уровне вне какой-либо функции, и там нет переменной foo.

Я могу понять, почему это может сбивать с толку, потому что, если вы посмотрите на стек выполнения, это выглядит так:

JavaScript execution stack

Таким образом, легко предположить, что «родительский» контекст выполнения является следующим в стеке, ниже текущего. Однако это не так.

В первом примере контекст выполнения a() действительно является «родительским» контекстом выполнения b(). Не потому, что a() является следующим элементом в стеке выполнения, чуть ниже b(), а просто потому, что b() объявлен внутри a().

Во втором примере стек выполнения выглядит так же, но на этот раз «родительский» контекст выполнения b() является глобальным контекстом выполнения, потому что b() объявлен на глобальном уровне.

Просто помните: не важно, где вызывается функция, важно, где она объявлена.

Но что произойдет, если она не сможет найти переменную в области «родительского» контекста выполнения?

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

Если его там тоже нет, он попробует следующий и т. д., пока в конце концов не достигнет глобальной области контекста выполнения. Если он не сможет найти его там, он выдаст ReferenceError.

Это называется цепочкой областей действия (scope chain), и это именно то, что происходит в следующем примере:

Сначала он пытается найти foo в области контекста выполнения c(), затем b(), а затем, в конце концов, a(), где он его найдет.

Примечание: Помните, что движок переходит только от c() к b() и a(), потому что они объявлены внутри другого, а не потому, что их соответствующие контексты выполнения располагаются поверх другого в стеке выполнения.

Если они не будут объявлены внутри другого, тогда «родительский» контекст выполнения будет другим, как объяснено выше.

Однако, если бы внутри c() или b() была другая переменная foo, ее значение было бы выведено на консоль, потому что движок прекратит «поиск» «родительских» контекстов выполнения, как только найдет переменную.

То же самое относится и к функциям, а не только к переменным, и то же относится к глобальным переменным.

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

Замыкание (Closure)

Замыкание обеспечивает доступ к области действия внешней функции из внутренней функции.

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

Это именно то, что происходит в приведенном выше примере. b() объявлена внутри a(), поэтому она может получить доступ к переменной name из области видимости a() через цепочку областей видимости.

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

Переменная c является просто ссылкой на внутреннюю функцию b(), поэтому последняя строка кода фактически вызывает внутреннюю функцию b().

Вы можете прочитать больше о том, как вы можете использовать замыкания в статье на Medium, написанной Эриком Эллиоттом.

Значение this

Следующее, что определяется на этапе создания контекста выполнения, это значение this.

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

Я постараюсь просто объяснить, и, кстати, вы можете найти более подробную статью по этой теме на MDN.

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

Или стрелочной функций:

Функция стрелок

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

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

Значение this будет таким же, как значение this в родительском контексте выполнения.

Мы можем увидеть это в двух примерах ниже.

Первый из них будет записывать true, а второй – false, хотя myArrowFunction вызывается в одном и том же месте в обоих случаях. Единственное различие между ними заключается в том, где была объявлена стрелочная функция.

Поскольку значение this внутри myArrowFunction является лексическим, в первом примере это будет window, поскольку оно объявлено на глобальном уровне вне какой-либо функции или класса.

Во втором примере значение this внутри myArrowFunction будет значением this внутренней функции, которая его оборачивает.

Я расскажу о том, что именно это значение, позже в этом разделе, но пока достаточно заметить, что это не window, как в первом примере.

Помните: для функций со стрелками значение this определяется в зависимости от того, где объявлена стрелочная функция, а не где и как она вызывается.

Объявления функций

В этом случае все не так просто, и это как раз причина (или, по крайней мере, одна из них), почему стрелочные функции были введены в ES2015.

Помимо разницы в синтаксисе между функциями стрелок (const a = () => <…>) и объявлениями функций (function a () <…>), значение this является основным отличием между ними.

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

  • Простой вызов: myFunction()
  • Вызов метода объекта: myObject.myFunction()
  • Вызов конструктора: new myFunction()
  • Вызов обработчика событий DOM: document.addEventListener(‘click’, myFunction)

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

Простой вызов

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

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

Но помните, это верно только для простого вызова; за именем функции следует ().

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

Вот почему в строгом режиме (strict mode) значение this внутри любой функции, вызываемой простым вызовом undefined, и в приведенном выше примере будет выведено значение false.

Вызов метода объекта

Когда свойство объекта объявлена как функция в качестве значения, оно считается методом этого объекта, отсюда и термин вызов метода (method invocation).

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

Примечание. Если бы использовался синтаксис стрелочных функций, вместо объявления функции, значением this функции внутри был бы объект window.

Это потому, что его родительский контекст выполнения был бы глобальным контекстом выполнения. Тот факт, что он объявлен внутри объекта, ничего не меняет.

Вызов конструктора

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

Когда функция вызывается таким образом, функция возвращает новый объект (даже если у него нет оператора return), и значение this внутри функции будет указывать на этот вновь созданный объект.

Объяснение немного упрощено (подробнее тут в MDN).

Примечание. То же самое относится и к использованию ключевого слова new в классе, поскольку class на самом деле являются синтаксический сахар.

Примечание. Стрелочные функции нельзя использовать в качестве конструктора.

Вызов обработчика событий DOM

Когда функция вызывается как обработчик события DOM, значением this внутри функции будет элемент DOM, в который было помещено событие.

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

Вызов с пользовательским значением this

Значение this внутри функции может быть явно установлено, вызывая функцию с помощью bind(), call() или apply() из Function.prototype.

В приведенном выше примере показано, как работает каждый из них.

call() и apply() очень похожи, единственное отличие состоит в том, что с apply() аргументы функции передаются в виде массива.

Хотя call() и apply() фактически вызывают функцию со значением this с тем что вы передаете в качестве первого аргумента, bind() не вызывает функцию.

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

Вот почему вы видите (5, 6) после a.bind (obj), чтобы фактически означает вызов функции, возвращаемую bind().

В случае bind() значение this внутри возвращаемой функции постоянно связано с тем, что вы передаете в качестве значения this (отсюда и имя bind()).

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

Но есть и исключение из правил, и это исключение является вызовом конструктора. При вызове функции таким способом, помещая ключевое слово new перед вызовом, значением this внутри функции всегда будет объект, возвращаемый вызовом, даже если новой функции было дано другое this с помощью bind().

Вы можете проверить это в следующем примере:

Вот пример того, как вы будете использовать bind() для управления значением this для обработчика события click, который мы обсуждали ранее:

Примечание: bind(), call() и apply() не могут быть использованы для передачи пользовательского значения this стрелочным функциям.

Примечание о стрелочных функциях

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

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

Вот почему люди, которые пишут спецификации для JavaScript, придумали стрелочные функции, где значение this всегда лексическое и всегда определяется одинаково, независимо от того, как они вызываются.

Поднятие (Hoisting)

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

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

Только на втором этапе (этапе исполнения) им присваивается фактическое значение, и это происходит только при достижении строки назначения.

Вот почему следующий код JavaScript выведет undefined:

На этапе создания переменная a идентифицируется, и ей присваивается специальное значение undefined.

Затем, на этапе выполнения, достигается строка, которая выводит на консоль a значение undefined, так как она было установлено в качестве значения на предыдущем шаге.

Когда будет достигнута строка, где ей назначено значение 1, значение a изменится на 1, но значение undefined уже было выведено в консоли.

Этот эффект называется подъемом (hoisting), как если бы все объявления переменных были подняты в верхнюю часть кода. Как видите, это не совсем то, что происходит, но именно этот термин, используется для его описания.

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

Примечание. При попытке получить доступ к переменной, которая вообще не была определена, выдается ReferenceError: x is not defined. Таким образом, существует разница между «undefined» и «not defined», что может немного сбивать с толку.

Заключение

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

Я надеюсь, что эта статья сделает то же самое для вас!

Илон Маск рекомендует:  Что такое код session_is_registered
Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL