Содержание
Функции
В этой статье описаны функции Javascript на уровне языка: создание, параметры, приемы работы, замыкания и многое другое.
Существует 3 способа создать функцию. Основное отличие в результате их работы — в том, что именованная функция видна везде, а анонимная — только после объявления:
Именованные (FunctionDeclaration) | Анонимные (FunctionExpression) |
---|---|
function имя(параметры) { … } | var имя = function(параметры) { … } … var имя = new Function(параметры, ‘…’) |
Именованные функции доступны везде в области видимости | Анонимные — доступны только с момента объявления. Синтаксис new Function используется редко, в основном для получения функции из текста, например, динамически загруженного с сервера в процессе выполнения скриптов. |
/* функция sum определена ниже */ var a = sum(2,2) function sum(x,y) { return x+y } | /* будет ошибка, т.к sum еще не существует */ var a = sum(2,2) var sum = function(x,y) { return x+y } |
В javascript функции являются полноценными объектами встроенного класса Function. Именно поэтому их можно присваивать переменным, передавать и, конечно, у них есть свойства:
function f() { ... } f.test = 6 ... alert(f.test) // 6
Свойства функции доступны и внутри функции, так что их можно использовать как статические переменные.
Например,
function func() { var funcObj = arguments.callee funcObj.test++ alert(funcObj.test) } func.test = 1 func() func()
В начале работы каждая функция создает внутри себя переменную arguments
и присваивает arguments.callee
ссылку на себя. Так что arguments.callee.test
— свойство func.test
, т.е статическая переменная test.
В примере нельзя было сделать присвоение:
var test = arguments.callee.test test++
так как при этом операция ++
сработала бы на локальной переменной test
, а не на свойстве test
объекта функции.
Объект arguments
также содержит все аргументы и может быть преобразован в массив (хотя им не является), об этом — ниже, в разделе про параметры.
Каждая функция, точнее даже каждый запуск функции задает свою индивидуальную область видимости.
Переменные можно объявлять в любом месте. Ключевое слово var
задает переменную в текущей области видимости. Если его забыть, то переменная попадет в глобальный объект window
. Возможны неожиданные пересечения с другими переменными окна, конфликты и глюки.
В отличие от ряда языков, блоки не задают отдельную область видимости. Без разницы — определена переменная внутри блока или вне его. Так что эти два фрагмента совершенно эквивалентны:
Заданная через var
переменная видна везде в области видимости, даже до оператора var
. Для примера сделаем функцию, которая будет менять переменную, var
для которой находится ниже.
Например:
function a() { z = 5 // поменяет z локально.. // .. т.к z объявлена через var var z } // тест delete z // очистим на всякий случай глобальную z a() alert(window.z) // => undefined, т.к z была изменена локально
Функции можно запускать с любым числом параметров.
Если функции передано меньше параметров, чем есть в определении, то отсутствующие считаются undefined
.
Следующая функция возвращает время time
, необходимое на преодоление дистанции distance
с равномерной скоростью speed
.
При первом запуске функция работает с аргументами distance=10
, speed=undefined
. Обычно такая ситуация, если она поддерживается функцией, предусматривает значение по умолчанию:
// если speed - ложное значение(undefined, 0, false...) - подставить 10 speed = speed || 10
Оператор ||
в яваскрипт возвращает не true/false
, а само значение (первое, которое приводится к true
).
Поэтому его используют для задания значений по умолчанию. В нашем вызове speed
будет вычислено как undefined || 10 = 10
.
Поэтому результат будет 10/10 = 1
.
Второй запуск — стандартный.
Третий запуск задает несколько дополнительных аргументов. В функции не предусмотрена работа с дополнительными аргументами, поэтому они просто игнорируются.
Ну и в последнем случае аргументов вообще нет, поэтому distance = undefined
, и имеем результат деления undefined/10 = NaN
(Not-A-Number, произошла ошибка).
Непосредственно перед входом в тело функции, автоматически создается объект arguments
, который содержит
- Аргументы вызова, начиная от нуля
- Длину в свойстве
length
- Ссылку на саму функцию в свойстве
callee
Например,
function func() { for(var i=0;i<arguments.length;i++) { alert("arguments["+i+"] = "+arguments[i]) } } func('a','b',true) // выведет // arguments[0] = a // arguments[1] = b // arguments[2] = true
Свойство arguments
похоже на массив, т.к у него есть длина и числовые индексы. На самом деле arguments
не принадлежит классу Array
и не содержит его методов, таких как push
, pop
и других.
Если все же хочется воспользоваться этими методами, например, чтобы вызвать другую функцию с теми же аргументами, но кроме первого, можно создать из arguments
настоящий массив:
var args = Array.prototype.slice.call(arguments) // .. теперь args - настоящий массив аргументов .. args.shift() ...
Вызвать функцию для массива аргументов можно при помощи apply
:
var func = function(a,b) { alert(a+b) } var arr = [1,2] func.apply(null, arr) // => alert(3)
Функцию легко можно передавать в качестве аргумента другой функции.
Например, map
берет функцию func
, применяет ее к каждому элементу массива arr
и возвращает получившийся массив:
var map = function(func, arr) { var result = [ ] for(var i=0; i<arr.length; i++) { result[i] = func(arr[i]) } return result }
Пример использования:
map(run, [10, 20, 30]) // = [1,2,3]
Или можно создать анонимную функцию непосредственно в вызове map
:
// анонимная функция утраивает числа map( function (a) { return a*3 } , [1,2,3]) // = [3,6,9]
Бывают функции, аргументы которых сильно варьируются.
Например:
// можно указать только часть аргументов // не указанные - вычисляются или берутся по умолчанию function resize(toWidth, toHeight, saveProportions, animate) { // значения по умолчанию saveProportions = saveProportions || true animate = animate || true toHeight = toHeight || ... }
Вызов с необязательными параметрами приходится делать так:
resize(100, null, null, true)
Чтобы избежать лишних null
и сделать код более понятным, используют нечто вроде «keyword arguments», существующих в Python и Ruby. Для этого много параметров пакуют в единый объект:
function resize(setup) { // значения по умолчанию var saveProportions = setup.saveProportions || true var animate = setup.animate || true var toHeight = setup.toHeight || ... }
Вызов теперь делается гораздо проще:
var setup = {toWidth: 100, animate: true} resize(setup) // или resize({toWidth: 100, animate: true})
Так — куда понятнее. А если параметров больше 5, то вообще — единственный нормальный способ.
Кроме того, с объектом можно удобнее делать последовательности вызовов вроде:
var setup = {toWidth: 100, animate: true, saveProportions: false} resize(setup) setup.toWidth = 200 resize(setup)
JavaScript Essential. Видео урок «Функции»
После просмотра этого видео урока Вы узнаете, что такое функция. Также в этом уроке Вы узнаете о особенностях использования функций в JavaScript и изучите такие конструкции как функции-литералы, функции обратного вызова. В этом уроке будут рассмотрены особенности языка связанные с областями видимости, поэтому для понимания принципов разработки сложных JavaScript модулей этот урок будет очень важным.
Функции – это блок кода, который можно многократно вызывать для выполнения. Функция может принимать аргументы и возвращать значения. Я думаю, что многие из слушателей этого видеокурса сталкивались с такими понятиями как функции и процедуры. На всякий случай давайте мы попробуем еще раз с самого начала объяснить и понять, как работают функции, какое их назначение в программном коде. Функция по сути является основным строительным блоком любого приложения. Если вы посмотрите на этот слайд, то вот этот прямоугольник посредине слайда, он у нас символизируем функцию, то есть это некий черный ящик. Мы даем ему какую-то информацию на вход, в этом черном ящике происходят какие-то операции и что-то появляется на выходе, какой-то результат. Вот получается, мы в функцию передаем сейчас, вот эта иконка символизирует у нас один байт информации, вот со значением один в двоичной системе исчисления. Вот мы в функцию передали это значение, вот эти колесики, они символизируют какие-то действия, происходящие внутри функции, то есть функция произвела какие-то вычисления, что-то произошло с этим значением, и в итоге функция на выход дала какое-то новое значение. Бывают функции, которые принимают данные и выдают какой-то результат. Бывают функции, которые ничего не принимают и выдают какой-то результат. Бывают функции, которые ничего не принимают и ничего не выдают. Бывают функции, которые что-то принимают, но ничего не возвращают. То есть, есть несколько разновидностей функций, мы сейчас их в коде всех с вами разберем. Но перед тем, как переходить к коду, давайте попробуем понять назначение функций на каком-нибудь простом, жизненном примере. Представьте, что вам, в вашем приложении нужно описать процесс заварки чая. Вам нужно будет в JavaScript сценарии несколько раз в разных частях программы вашей выполнять заварку чая. Из чего состоит заварка чая? Первое – нужно пойти на кухню. Второе – нужно взять чашку и засыпать туда сахар и чай. Третья операция – закипятить воду. Четвертая – залить воду в чашку. Вот 4 операции и чай готов. Если вам нужно в вашем приложении чай заваривать 10 раз, вот вам придется в 10 разных частях программы вызвать 4 инструкции для того, чтобы заварить чай. При этом вам нужно следить, чтобы вы не напутали с последовательностью инструкций, потому что если вы в начале зальете воду, потом закипятите ее, это уже будет не совсем чай, неправильно он будет сделан. Получается, что в тех ситуациях, когда у нас есть несколько инструкций, какой-то блок кода, который повторяется в разных частях программы, вот этот блок кода можно превратить в функцию. Например, вот эти 4 инструкции, для заварки чая, их можно вынести в функцию и дать функции имя «Заварить чай». А те части программы, в которых мы раньше пытались заваривать чай, используя 4 инструкции, 4 команды последовательные, вот эти 4 команды можно удалить и вместо них установить запуск функции «заварка чая». Я верю, что после просмотра примеров, назначение функций всем будет понятней.
Давайте сейчас посмотрим второй слайд, посмотрим, как именно функция определяется в JavaScript коде, потом перейдем в Visual Studio и посмотрим на несколько первых примерах. Когда мы хотим определить в коде функцию, первое, что мы должны сделать – это использовать специальное ключевое слово. Слова function. Наличие этого слова в коде указывает, что мы сейчас создаем новую функцию, или объявляем функцию. После слова function в большинстве случаев следует имя. В некоторых ситуациях имя не указывается, мы рассмотрим такие варианты функций, но сейчас на слайде вы видите, что сразу после function, тут же идет фраза, литерал, который будет указывать имя функции, обычно функцию называют не так, как на нашем слайде, myFunction – это имя, которое ничего не означает. По имени не можем понять, а что именно делает функция. Хорошее имя для функции – это глагол, описывающею что именно функция будет выполнять, когда мы ее вызовем. Например, функция будет называются getUserName. Сразу же понятно назначение функции. Если мы ее запустим, то она отдаст нам имя пользователя. Или спросит имя у пользователя, или спросит имя у пользователя, или получит его с каких-то внутренних переменных. Другой вариант функции – Calculate, CalculateSum, например, то есть посчитать сумму. Понятно, что если мы эту функцию запустим, то получим сумму каких-то значений. После имени функции, у нас установлены параметры, параметры – это то, что функция принимает для обработки. Если нашей функции при запуске необходимы какие-то значения, эти значения функции могут быть переданы с помощью параметров. Подробней с параметрами мы познакомимся с вами уже в примерах.
Давайте сейчас посмотрим первые примеры использования функции в Visual Studio. Значит, как мы уже посмотрели на слайдах, определение функции всегда начинается с ключевого слова function. Вот на строке 21 мы создаем функцию, создаем приложение, создаем строительный блок, который будем потом неоднократно использовать. Мы указали ключевое слово, далее мы указали имя функции – MyFirstFunction(). Так как эта функция не принимает никаких входящих данных, она не должна производить расчеты над какими-то значениями, мы просто после имени ставим круглые скобочки. Это означает, что функция не принимает никаких параметров. И далее в теле функции мы выполняем три инструкции, в тело документа выводим сообщение «Hello from MyFirstFunction!, How are you?, Goodbye!». Вот эти три строчки появляться в документе если мы запустим эту функцию. Мы сейчас просто создали функцию, то есть это просто заготовка, которую потом нужно вызвать, выполнить. Получается, когда интерпретатор, когда браузер дойдет до 21 строки. Существует такая функция, он не будет выполнять этот код, потому что определение функции, это просто создание инструкций, которые в будущем кто-то начнет использовать. Так же в нашем примере есть еще одна функция, на 27 строке, эта функция называется MySecondFunction(), и она выводит только одно сообщение «Hello from MySecondFunction!». Это все что она делает. Точно так же, как и предыдущая функция, это всего лишь объявление. То есть это блок кода, который не будет выполнятся до тех пор, пока мы не обратимся к функции и не запустим ее, не вызовем функцию. Значит 21-22 строка – мы объявили функцию. Теперь как мы этими функциями пользуемся. 33 строчка. Мы берем имя функции MyFirstFunction() уже без ключевого слова function, только имя и круглые скобочки. Когда мы используем такой синтаксис – имя функции + ее параметры, это означает, что мы обращаемся к функции и вот на строе 33, как только мы выполним эту операцию, это означает, что с 33 строки мы перепрыгиваем на строку 21. Выполняем инструкции на 22, 23, 24 строчке, и потом, когда мы эти инструкции до конца выполняем, мы возвращаемся обратно, перепрыгиваем на 33 строку и продолжаем выполнять инструкции, которые идут вот в этой части кода. Выполняем 35 строчку, вводим горизонтальную линию. Потом на 37 строчке мы видим, что мы взываем вторую функцию MySecondFunction(). То есть на 37 строке мы прыгаем на строчку 27, заходим в тело функции, вот операторные скобки, выполняем инструкцию, которая здесь заложена. Как только мы до конца выполняем все инструкции, заложенные в функции, мы возвращаемся обратно в ту точку, где функция изначально была запущена. Давайте попробуем проверить, что получится. Вот вы видите, первый раз мы на 33 строке вызвали функцию и отобразились те сообщения, которые заложены внутри этой функции, вот все эти инструкции, код весь этот выполнился. Потом 35 строчка, мы вывели горизонтальную линию. И строка 37 мы еще раз запустили функцию, уже вторую функцию и вот увидели ее результат. Если мы хотим несколько раз повторить вот этот блок кода, несколько раз вывести эти три сообщения, можем взять и просто продублировать вызов функции. Вот мы сейчас три раза запускаем MyFirstFunction(), то есть в приложении, на 33 строке мы пригнем на строку 21, выполним все что заложено в функции, вернемся обратно сюда. Перейдем на 34 строчку и увидим, что на ней опять происходит вызов функции. На 34 строке мы пригнем на 21 строчку, выполним все, что заложено в функцию и вернемся обратно на 34. На 35 опять пригнем в функцию, выполним все, что заложено и вернемся на 35. То есть каждый раз, когда мы вызываем функцию, мы производим переход в другую часть программы, выполняем инструкции, заложенные в этой другой части программы и возвращаемся обратно, где функция была изначально запущена. Давайте сейчас запустим и проверим, первый раз вызвалась наша функция, второй раз и третий раз. Вот пример, который я приводил с заваркой чая. Вместо того, чтобы здесь дублировать инструкции заварки чая, мы эти инструкции вынесли в отдельную функцию и каждый раз, когда нам хочется чая, мы берем и запускаем функцию сделать чай и не задумываемся о тех шагах, которые нужно выполнить, для того, чтобы появилась чашка чая. Перед тем, как переходить к следующим примерам, хочу, чтобы мы еще посмотрели, как можно пользоваться отладчиком и посмотреть, как функции работают именно под отладчиком. Лично мне удобней пользоваться отладчиком, который работает в Google Chrome, но как мы уже разбирали на прошлых уроках, если вы возьмете Firefox, например, с установленными Fireback флаером, или Internet Explorer, или Opera, во всех современных браузерах есть инструменты разработчика, поэтому просто выберете тот браузер, которым вам пользоваться нравится больше всего и в нем попробуйте разобраться с инструментами разработчика. Если мы сейчас запустим этот пример, в Chrome, если мы нажимаем на F12 в браузере это открывает панель разработчика. Вот панель разработчика, нас интересует сейчас вкладочка source. Вот когда вы впервые запустите свое приложение, вы увидите, что вот на этой вкладочке ничего нету, будет пусто, никаких исходных кодов текущего документа отображаться не будет. Если нажать по этой кнопочки showNavigator, то вы увидите те сценарии, которые доступны сейчас в текущем документе. Мы выбираем сейчас сам HTML документ, переходим в его исходный код, находим интересующею для нас функцию, вот мы хотим начать отладку с 33 строки. Ставим здесь breakpoint. Мы на прошлых занятиях разбирали с вами что такое breakpoint. Поставили breakpoint. Для того, чтобы теперь этот breakpoint сработал, чтобы эта точка остановила код и показала нам пошаговое выполнение скрипта, мы можем сейчас обновить страницу, видите сейчас вот это синий блок, красная стрелочка, которая находится рядом с ним, эта стрелочка символизирует ту часть кода, которую сейчас браузер выполняет. Вот сейчас мы возьмем с вами и если будем нажимать на кнопку F10 или на эту иконку, это означает, что мы переходи пошагово, построчно. Просто выполняем все инструкции. Если мы будем нажимать на кнопку F11 или на эту иконку, это будет означать, что при вызове функции мы будем переходить внутрь функции и смотреть, что будет происходить в самой функции. Вот если мы сейчас нажмем на F11, так как вот здесь мы взываем функцию myFunction, мы должны перейти внутрь функции, выполнить весь код, который находится в функции и вернутся обратно на 33 строчку. Давайте попробуем. Нажимаем на F11, видите мы попали внутрь функции, на строчку 22. Нажимаем еще раз F11, еще раз, и вот когда мы закончим операцию на 24 строке, мы вернемся в ту часть кода, в которой функцию изначально вызвали. Нажимаем еще раз F11 и вот видите мы вернулись на строчку 34 из-за функции, которую изначально запустили. Если сейчас еще раз нажать F11. Так как на 34 строке опять запускается функция, мы вернемся в тело функции и выполним то что находится в теле функции. Нажимаем F11, попадаем в функцию, выполняем код, который заложен в функции, видите, код у нас выводится сразу же в самом документе по мере его выполнения, выполнили функцию, вернулись в ту часть кода, где функция запускалась. Следующая функция, нажимаем F11, попадаем в тело функции, и вот так далее. Вот если вы не понимаете, как работает сценарий, если вы хотите найти ошибки в сценарии, рекомендую пользоваться отладчиком, он всегда помогает сэкономить время и быстро найти ту часть кода, с которой что-то у вас работает некорректно.
В следующем примере мы увидим, как в функции можно использовать переменные, которые доступны в области видимости, глобальной области видимости. Подробней о областях видимости мы сейчас с вами поговорим, а вот пока разберем данный пример. То есть в этом примере мы разберем, что функции могут работать с переменными в своем теле. Сейчас у нас есть 2 функции, на 22 строчке ask и на 28 функция say. То есть «спросить» и «сказать». Функция ask будет запускать ряд prompt функций, то есть функция prompt, это функция, которая уже определена в браузере. То есть если вот эта функция, это основная функция, то функция prompt находится где-то в браузере, задача этой функции показать диалоговое окошко, получить от пользователя данные, обработать эти данные и вернуть в наше приложение, вернуть нашему сценарию уже. Если кто-то запустит функцию ask в нашем коде, то вначале мы получим от пользователя имя, потом фамилию, потом возраст. И эти полученные данные запишем в переменную name, sname и age. Переменные, которые мы используем в функции ask обвялены выше, на 18, 19, 20 строчке. Вот эти переменные объявлены здесь и соответственно они могут быть использованы как внутри функции ask, так и внутри функции say. Видите, что функция say будет последовательно отображать вначале имя, потом фамилию, потом возраст и выводить эту информацию в тело документа. Но как мы уже с вами обсуждали, определение функции не значит, что эти функции будут работать, просто определив функцию, мы определили строительный блок в нашем приложении. Тут этот строительный блок должен использовать, для того чтобы код в функции запустился. И вот на 37 строке мы первый раз вначале запускаем функцию ask, чтобы попросить пользователя заполнить значениями переменные. А второй раз на 38 строчке мы запускаем функцию say, чтобы значения, которые находятся в переменных отобразились пользователю на экран. Давайте запустим пример и проверим. Первое – введите свое имя, фамилию и возраст. Вот значения, которые мы ввели в поля ввода, вот они отобразились у нас на экране.
В следующем третьем примере мы по сути разбираем второй пример, который переделан на использование в цикле. То есть мы здесь будем неоднократно задавать пользователю вопрос, для того, чтобы как бы повторно выполнить несколько операций, несколько функций. Вначале сценария определяются те же функции, что и в предыдущем примере, но вот на 34 строке запускается цикл do..while, помните, что в цикле do..while у нас всегда выполняются первая итерация, а потом, в зависимости от условия мы либо продолжаем работать, либо повторяем итерацию цикла, либо выходим из цикла и продолжаем инструкции. Каждый раз, когда будет запускается цикл do..while, на каждой итерации мы будем запрашивать имя, фамилию, возраст. Отображать полученные данные, а потом на 38 строчке в переменную repeat записывать значения, которые пользователь выберет в диалоговом окошке с кнопками «Да» и «Нет». То есть мы спросим «Пройти заполнение еще раз?». Если пользователь нажмет на «Да», то в переменную repeat запишется значение true и мы на строке 38, условие у нас срабатывает, поэтому мы возвращаемся на начало и еще раз выполняем инструкцию. Если же пользователь выберет значение «Нет» — cancel нажмет, то repeat будет false, условие не срабатывает и наш сценарий выходит дальше, ну дальше нет инструкции, поэтому выполнение кода завершается. Давайте проверим. Вот «Введите свое имя, фамилию, возраст» и теперь смотрите, мы первый раз вывели, запустили первую-вторую функцию, ask и say. Теперь запускается функция confirm, которая спрашивает повторить или нет, если мы нажмем повторить, мы еще раз запускаем повторную функцию ask, она у нас спрашивает возраст и фамилию, потом нажимаем на Ок. Теперь, если мы нажмем на cancel, то у нас спрашивает повторно хотим ли мы заполнить эту анкету, если мы нажимаем на cancel, то цикл прекращается, повторно функцию мы уже не запускаем и прекращаем генерировать какой-то контент, в текущем документе. Вот получается, что использование функции, они даже упрощают понимание самого кода, нам не приходится вдавятся в детали, в подробности, мы можем просто запомнить, что делает каждая из этих функций и просто проанализировать алгоритм работы самого сценария. Вот у нас есть задание вопросов, потом что-то мы выводим пользователю, потом спрашиваем нужно ли повторить и выполняем повторение, если пользователь согласится с повтором. Но если бы у нас функции не было, то все, что находится внутри, детали реализации задания вопросов, детали реализации вывода результатов в документ, все эти детали находились в самом цикле do, и соответственно цикл был бы достаточно большим, достаточно сложно было бы его редактировать и контролировать, а так, когда у вас есть отдельные функции и у функции выражены четко обязанности функции, то вы может, если вы знаете, что нашли ошибку, в той части кода, в котором задают вопрос, то сразу понимаете куда нужно обратится, где нужно искать ошибку. Нужно открыть функцию ask и посмотреть, как она работает. Если вы зафиксировали ошибку в выводе пользователю данных, вы берете функцию say и проверяете, что в этой функции было написано неправильно. Если же у вас функции нету и все выполняется единым блоком кода, там несколько сотен строчек, то в таком случае вам нужно будет ковыряться вот в всем этом коде и искать где там что происходит. Поэтому функции, они очень часто помогаю сделать код более логическим, структурированным, сделать так, чтобы его потом проще сохранить. Если вам нужно пользоваться функциями, то сценарий становится упорядоченным и дальше с ним будет намного легче работать. В следующих примерах мы посмотрим, как можно задавать функциям аргументы. Как можно сделать функцию, которая при запуске будет получать от пользователя какие-либо данные. Как мы уже говорили, параметры функции определяются после имени функции, вот на 22 строке функция, как и в предыдущих наших примерах не принимает никаких параметров или аргументов. Аргументы и параметры – это одно и тоже самое. Вот на 20 строчке функция не принимает аргументов, а на строке 25 функция принимает 2 аргумента или 2 параметра. Первый параметр с именем х1, второй параметр с именем х2. Давайте пока разберем первую функцию, что она делает. В функции самм1 на строке 21 создается переменная result и в эту переменную записывается сумма двух других переменных, которые определены выше, на строке 15 и 16. Получается, что в переменную result будет записана сумма 25. И эту сумму на 22 строке мы отобразим в тело документа. На строке 26, когда мы разбираем эту функцию, мы не можем заранее знать какой результат будет отображается этой функцией, потому что при загрузке функции — вот эти переменные х1 и х2, в эти параметры могут быть заданы какие-либо значение. Какие именно мы сейчас не знаем, потому что выше таких переменных х1 и х2 у нас не объявлено. Получается, что кто-то запуская эту функцию передаст сюда значение. Например, в х1 передаст значение 1, в х2 значение – 4, например. Вот здесь на 26 строчке 1 + 4 даст результат 5. Этот результат будет записан в переменную result и в итоге отображен на 27 строке. Вот мы увидели 2 объявление функции. Давайте теперь разберем как эта функция запускается. Разницу вызова. На строке 33 мы берем sum1 и {} после sum2. Этот вызов правильный, потому что сама функция ничего не принимает, не требует никаких от пользователя значений. Под пользователем сейчас подразумевается разработчик, который запускает эту функцию. И вот сейчас на 33 строке у нас запускается функция и на экране мы выводим результат, результат 25. На строке 25 мы запускаем функцию sum2. И тоже не переедаем ей никаких параметров, это у нас не правильно потому что функция sum1 при определении требует два аргумента х1 и х2. Если мы не будем при вызове передавать никаких значений, то это будет означать, что х1 и х2 будут неопределенны. А мы уже с вами знаем, что когда будет, когда создаем переменную и не указываем значение для этой переменной. У переменной значением по умолчанию будет undefined. Давайте посмотрим, что получится если запустить такую функцию. Видите, первый раз мы увидели 25, когда вызвали sum1, а второй раз, когда мы вызвали саааамммммм2, мы результат увидели, как none. Потому что undefined + undefined получается непонятно что, not a number. Вот мы выводим этот результата. Вот когда на 36 строке мы запускаем функцию sum2, мы уже пользуемся ею корректно, мы передаем ей два значение, мы указываем, что первое значение будет значение переменной “c”. Переменная “c” у нас находится на 17 строке, в ней находится значение 20. Нас троке 36 у нас находится значение переменной “d”. Это значение придется как второй аргумент. В переменной “d” находится значение 21. Сейчас, на 36 строке, когда запускается эта функция, мы переходим на 25 строчку, здесь в х1 находится значение 20, в х2 находится значение 21. Соответственно сумма 21 и 20 дает результат 41, который мы выводим на экран. Видите, 41 отобразилось у нас в документе. При запуске функции необязательно передавать значения, которые взяты из переменных, мы можем прямо здесь установить значение 2 и 10, например. Вот сейчас, если мы запустим вот такую функцию, то результат, который выведется в документ, будет равен 12. Проверяем. Получилось 12. Давайте еще раз посмотрим, как это будет выглядеть в отладчике. Нажимаем F12, берем source, navigator, выбираем текущий документ, переходим к той части кода, которая нас интересует, вот нас интересует сейчас 36 строка. Ставим здесь breakpoint, нажимаем refresh, вот мы остановились на этой функции, остановили ее, давайте нажмем сейчас step_into, или F11, мы перепрыгнули функцию, если сейчас мы наведем мишкой на х1, Chrome подскажет, что в этой переменной значение 2, в х2 – значение 10. В соответствии с теми значениями, которые мы передавали при вызове. Эти значения попали в эти два параметра, или в эти два аргумента и теперь на 26 строке мы эти значения сложили, можем сейчас нажать stepinto или stepover, сейчас без разницы, потому что эта строчка не является вызовом функции. Давайте нажмем на F10, и переходим на следующею строку. Если наводим на result, видим, что Chrome подсказывает, что значение переменной 12. Значение мы дальше отображаем в тело документа. Его выведем сейчас. Вот 12 у нас отобразилось. Не забывайте пользоваться отладчиком, когда вы чего-то не понимаете, старайтесь с помощью отладчика выяснить что происходит внутри сценария.
В следующем пример мы посмотрим, как можно сделать так, чтобы функция требовала, чтобы одни параметры обязательно к ней были заданы, а другие параметры были опциональны. Вот сейчас на 15 строчке мы хотим сделать так, чтобы функция print принимала два аргумента. Первый аргумент- был обязательно, а второй аргумент, если его не предоставят, то функция возьмет свое какое-то значение по умолчанию. Задача функции print отображать в документе несколько сообщений, сообщений, которые были переданы первому аргументу. Вот если кто-то запускает эту функцию, первое, что делает функция – это 17 строчка. Проверят было ли предоставлено значение в параметр count. Если count равен undefined, то строка 18 мы говорим, что count будет равен значению три. То есть по умолчанию у нас всегда функция принт будет с count равен трем. Но если при запуске сюда передать значение, то на 17 строке условие не срабатывает и count будет равен тому значению, которое передал пользователь при вызове. Дальше после проверки на 21 строке мы запускаем цикл фор на количество итераций, равной значению переменной count. То есть count раз, на 22 строчке мы выводим сообщение переменной msg, то есть message. А в конце, когда все это заканчивается на 25 строке мы выводим horizontalRool. Вызов функции на 31 строчке, мы вызываем функцию только с один значение, со значением Hello, это значение первое, соответственно оно адресовано первому аргументу, переменной msg. НА 31 строке мы увидим, что сообщение Hello отобразится три раза, потому что count по умолчанию будет равен трем. На 32 строке, когда мы вызываем print двумя параметрами, двумя значениями world и семь. Это означает, что msg будет world, count будет равен семи. Соответственно условие не срабатывает и цикл срабатывает семь раз со значением, которое ввел пользователь. Hello три раза в соответствии со значением по умолчанию, world – семь раз. Вот этот пример показывает, как можно создать функцию и определить в функции опциональные параметры, которые не обязательны при запуске. Очень часто разработчики, которые создаю функцию с функциональными параметрами, добавляют вот такой комментарий, просто пишут необязательный параметр, или опциональный параметр для того, чтобы было понятно, что эта переменная, этот аргумент, он не обязательный при вызове. Все функции, которые мы сейчас с вами посмотрели, по сути они были процедурами, вот если вы работали с другими языками программирования, то вы сталкивались с таким термином как функция и процедура. Функция – это конструкция, блок кода, который мы запускаем, блок кода что-то делает, а потом возвращает нам результат. А процедура – это такой же блок кода, который мы запускаем, что-то происходит в этом блоке кода, но в результате ничего нам как клиентам этого блока кода, как запускающей стороне, нам никакой результат е возвращается. Вот мы с вами видели, что все наши примеры, в этих примерах функции не возвращали никаких данных в ответ. Мы их запустили, а в ответ ничего не получили. На самом деле в JavaScript нет такого понятия как процедуры, потому что любая функция всегда возвращает нам значение. Вот сейчас, у нас у всех примерах любая функция всегда возвращала значение undefined, просто мы им не пользовались и его игнорировали. Но запомните, что любая функция, если она не возвращает явно никаких данных, она вернет значение undefined. Давайте сейчас поострим как можно сделать функции, которые будут возвращать то что они рассчитали, то что они выполнили по окончанию своей работы.
Вот в следующем №6 примере мы посмотрим, как используется ключевое слово return и что оно делает. На строке 16 мы объявляем функцию sum, которая будет суммировать два входящих параметра «а» и «б». Но мы хотим сделать так, что функция, получив значение и посчитав сумму, не выводила сразу же результат в тело документа, а возвращала результат, которой получила. Мало ил как мы захотим с этим результатом работать, может мы не хотим его отображать в документе, может мы alert вывести, или мало что можно еще делать. Для того, чтобы функция просто вернула результаты своей работы, на 17 строке мы используем ключевое слово return, и после этого слова указываем то значение, которое функция должна будет вернуть после своей работы. Если запустить функцию sum, она у нас выполнит вычисления, отдаст результат своих исчислений, отдаст сумму двух входящих параметров. Вот когда мы пользуемся такими функциями, строка 20, нам необходимо использовать функции вот в таком ключе, таким синтаксисом. Мы создаем переменную, а потом эту переменную инициализируем результатом работы функции. Если мы запускаем любую функцию, которая есть в JavaScript коде, эта функция всегда что-то возвращает, и это возвращаемое значение мы можем присвоить переменной. Вот сейчас возвращаемое значение функции sum будет суммой двух аргументов, два и три. Поэтому в переменную рез запишется результат 5, результат на 22 строке мы отобразим. Давайте проверим. Вот отобразилось значение 5. Если мы сейчас возьмем и, например, поменяем 18 строчку, наберем ключевое слово return, функция у нас уже будет выполнять возврат значений. Теперь это значение будет undefined, потому что это значение по умолчанию. Если разработчик не указал, что функция возвращает, то функция всегда будет возвращать undefined. Давайте запустим. Видите, результат работы функции undefined. Поэтому если вы не написали return, или ключевое слово return просто не выполнилось в теле функции, это означает, что функция вернет значение undefined. И еще, то что вы должны понимать, ключевое слово return прекращает выполнение функции. Если после этого ключевого слова мы напишем сообщение, это сообщение у нас не выведется, потому что 17 строка, как только интерпретатор видит ключевое слово return и выполняет его, то вот на этом ключевом слове у нас и заканчивается работа функции. Мы из 17 строки перепрыгиваем в ту точку кода, где функция изначально запускалась, тот результат, который ключевым словом return был возвращен, мы записываем в переменную. Вот вернулась 5, а alert так и не отобразился.
Вот теперь небольшой такой пример с использованием нескольких функций, которые возвращают результат. Сейчас мы определили 4 функции, функция add, для сложения, функция sub, для получения разницы, mul – для умножения и div – для деления. В каждой функции мы выполняем арифметическую операцию и результат возвращаем с помощью ключевого слова return. После определения функций, на строке 32 от пользователя с помощью prompt мы получаем введите первое число, то есть первую цифру, операнд один, потом мы получаем знак операции, + — * / строка 34. Потом получаем операнд два, второе значение. И 35 строчка – создаем переменную, которая будет просто содержать в себе результат работы наших дальнейших вычислений. На 37-38 строке мы используя функцию parseInt, конвертируем значение, которое вводил пользователь в целочисленное, для того, чтобы все арифметические действия выполнялись как операции над числами, а не операции над строками. На 40 строчки создаем switch, для того, чтобы проверить какое значение было введено с помощью prompt на 33 строке. Если пользователь выбрал +, то 42 строчка в переменную result мы записываем значение, которое возвращает функция add, мы вызываем функцию, определенную выше, передаем в эту функцию два параметра, которые были получены от пользователя на 37-38 строке. И результат, который получится — записываем в переменную result, на 43 строчке у нас выполняется break, поэтому после этой строки мы выпрыгиваем за switch и отображаем надпись од 7 строки. Если у нас результат не undefined, потому что, мало ли мы не попали ни в один из case, если мы вот с брейка перепрыгнем сюда, то вот тогда мы отобразим, что вот такой операнд + символ + второй операнд = значение. Если мы сейчас выберем, например, нажмем не на +, вот здесь в переменную sun поместим не +, а допустим значение «*» — умножить, то тогда на 40 строчке, когда будет работать switch, мы перепрыгнем на строку 48, на 48 строчку, выполним этот блок кода. Выполнится уже другая функция, которая получит 2 параметра и получит произведение 2 значений, это значение здесь запишется в result и здесь запишется. Давайте проверим как будет работать этот код. Введите первое число. Вводим два. Введите знак арифметической операции – вводим «+» и второе число – тоже два. Вот результат «2+2=4». Вводим 5*12, получается – 60. Вот такой простенький калькулятор написанный на JavaScript.
В следующем примере мы разберем пример с использованием нескольких аргументов. Если копнуть немножко глубже, то на самом деле функция в языке JavaScript – это не просто как бы синтаксическая единица, это тип данных. Если вы работали с языками там С++, Pascal, C#, любыми языками программирования, в большинстве языков функция это именно вот синтаксическая конструкция, это часть языка, с помощью которой мы определяем блок кода для повторного использования. А вот в JavaScript – функция – это тип данных и любая функция – это на самом деле – объект. Вот сейчас пока вы можете рассматривать функции как код для повторения, но если вы будут дальше работать с JavaScript кодом, если вы будут прослушивать следующий курс JavaScript углубленный, то вы там будете очень часто сталкивается вот с тем, что функция – это объект, а не просто кусочек кода. И вот сейчас мы тоже с вами увидим вот эту особенность функции в JavaScript коде, увидим, что функции, как объекты содержат в себе какие-то свойства. То есть функция кроме кода, который нужно выполнить, также в себе содержит разные значение, которые могут помочь создавать сложные и интересные сценарии. Сейчас у нас на 16 строке есть функции max, задача этой функции показать нам максимально значение того аргумента, который был передан при вызове. Вот если мы запустим функцию max и передадим в «а» значение 1, в «и» значение 10, а в «с» значение – 100, то функция вернет то значение, которое максимальное. Значение параметра «с», потому что там была записана сотня. Но также мы хоти сделать так, чтобы функция принимала у нас не только три параметра, а допустим 5, 10, 20, то есть неограниченное количество параметров. И в JavaScript это сделать очень легко, мы можем сделать так, чтобы функция принимала хоть 50 параметров, нам в принципе все равно. На 18 строке, когда эту функцию запустят, мы создадим переменную maxValue, вот в этой переменной будет находится максимальное значение, которое мы найдем в параметрах. Но изначально максимальным значением мы считаем минимальное значение, которое вообще может быть записано в целочисленную переменную. Мы записываем минус бесконечность. И потом посредством цикла, посредством проверок, мы это значение будем менять. На строке 19 вот мы изначально выводим какое значение записано в переменную maxValue, изначально мы записали NegativInfinity, то есть минус бесконечность. После вывода этого сообщение, на строке 21 запускается цикл и обратите внимание, на какое количество итераций. Мы берем переменную arguments и берем ее свойства length. Откуда взялась переменная arguments, если посмотреть вот в текущем коде, нигде такую переменную мы с вами не определяли. Вот как раз эта переменная, она у нас доступна сейчас в данном блоке кода, потому что функция – это не просто кусочек кода, это не синтаксическая единица языка JavaScript – это объект. Вот когда мы, вот сейчас выделенный код, который вы видите у себя, это есть код, который находится внутри объекта с именем макс внутри функции. И вот у объектов функции есть несколько свойств. Одно из свойств – это свойство arguments, которое в себе содержит все аргументы передаваемые функции при запуске. Вот сейчас, если мы запусти эту функцию вот таким вот способом, на строке 28 видите мы передаем 1, 2, 3, 4, 5. Пять значений. И вот получается сейчас, когда эта функция начинает работать, в переменной «а», «b», «с» находится -33, 33, 777. А вот остальные значения, они у нас не представлены параметрами, но они все равно доступны в функции, они как бы доступны функции через массив arguments. Массив arguments – это все значение, которые пришли в функцию при вызове. Вот сейчас в этом массиве, кроме первых трех значений, которые доступны так же через параметры, находится еще и два дополнительных значения, -666 и -22. Вот сейчас мы запускаем цикл на 21 строке, и на каждой итерации проверяем. Если arguments, вот как раз предыдущий урок, мы смотрели массивы, если и-тый элемент массива, на первой итерации – это -33. Если он больше, чем maxValue, а 33 будет больше чем минус бесконечность в любом случае, то есть если больше, то тогда 23 строчка, максимальное значение – оно у нас будет равно текущему аргументу – из массива arguments. То есть в maxValue мы записываем -33. Потом следующая итерация, мы берем следующий элемент из массива arguments 33, 33 больше чем maxValue? maxValue сейчас у нас -33, соответственно положительное 33 больше чем -33. maxValue теперь у нас новое значение, значение 33. Следующая итерация, здесь у нас 777, здесь 33. 777 больше чем 33, поэтому maxValue теперь у нас 777. Следующая итерация, здесь у нас находится -666, а maxValue у нас три семерки. Три семерки больше, условие не срабатывает, в условие мы не попадаем и maxValue не изменяем. То есть в итоге в переменной maxValue останется максимальное значение, которое при запуске было передано в параметры. Вот это максимально значение, которое по завершению этого цикла мы получим, это значение на 25 строке мы вернем с помощью ключевого слова return. И на 28 строчке видит, в переменную rez мы записываем тот результат, который был возвращен в функцию. Ну на 33 строке мы этот результат показываем в документ. В нашем случае, максимально значение сейчас будет 777. Вот это значение у нас вывелось. Если мы добавим еще несколько параметров, вот сейчас максимальное значение будет у нас 1000, это максимальное значение на 30 строке отобразится. Вот отобразилась 1000. В этом примере, что показывает этот пример, то что вам нужно запомнить, создавая функцию, вы можете сделать так, чтобы функция принимала неограниченное количество параметров, и вы в функции могли получать доступ ко всем этим параметров. Для этого вам необходимо пользоваться свойством arguments, которое доступно в теле функции. За пределами функции, это свойство у вас будет недоступным. Но когда вы работаете в этой функции, посредством этого свойства вы можете получать доступ ко всем параметрам, которые были переданы в функцию при запуске.
И последний, девятый пример из первой директории, показывает, как мы можем сделать функцию, которая будет проверять правильное ли количество параметров передали функции при запуске. Иногда нам важно, чтобы запускаемая функция принимала столько аргументов, сколько было указано при определении функции. Вот в предыдущем примере мы специально сделали так, чтобы функция принимала неограниченное количество параметров. Но сейчас мы хотим сделать именно так, чтобы в функции у нас принималось ровно три параметра, ни меньше, ни больше. Если количество параметров не совпадает с требованными, то наша функция вообще никаких операций не будет производить. Определяем функцию мы на 16 строке. Видите, что функция принимает скорей всего три координаты: х, у, z. На 19 строчке у нас идет проверка. Если arguments.lenght не равно 3, то выводим сообщение о ошибке. И больше по сути ничего не делаем, ведь этот блок кода у нас не срабатывает. Если же arguments равно 3, то условие на 19 строке не сработает, сработает блок else, увидим сообщение, что у нас корректно были получены данные и строка 25, запустив цикл for, все значения, который были получены в параметрах мы отобразим на экран через запятую. Вот посмотрите теперь на то как мы функцией пользуемся. На 30 строке мы запускаем функцию с именем f, то есть вот эту функцию и передаем 4 значения при запуске. Четыре значение — это перебор, наша функцию требует три значения, это условие на 19 строке срабатывает и выводит ошибку. А вот на строке 31, когда мы передаем три значения, функцию получив эти три значения не запускает if, попадает в блок else и соответственно срабатывает у нас как бы сама логика функции. Ну и 32 строчка, тоже функцию не срабатывает, так как количество параметров у нас меньше трех. Первый раз – ошибка. Четыре аргумента было получено, второй раз все нормально, вывелись все элементы, третий раз опять ошибка – у нас меньше аргументов чем надо, было получено два аргумента. Вот в первой части урока мы рассмотрели с вами как создаются функции, как сделать так, чтобы функция возвращала результат своей работы, как сделать так, чтобы функция принимала входные параметры и как можно сделать функцию, которая работает с неограниченным количеством параметров, для этого в функции есть свойство arguments. Эти примеры обязательно хорошо рассмотрите, изучите, потому что это основа работы с функциями.
Вторая часть урока будет посвящена областям видимости. Перед тем, как перейти к областям видимости мы повторим с вами что происходит с переменными, которые не создавали. Нам пригодится дальше для того, чтобы понимать следующий пример. На 13 строке мы создаем переменную «а». Помните, что если переменной мы не присваиваем значение, то значение этой переменной при чтении будет у нас undefined. Для остальных переменных мы задаем конкретное значение и соответственно переменные у нас инициализируются и получают какой-то определений тип данных. Переменная «b» со значением 777 она у нас будет number. На строке 15 переменная «c» использует значение переменной «b», соответственно это тоже будет number и тоже со значением 777. Переменная «d» будет типа null и содержать в себе единственное возможное для типа null значение null. Создав эти переменные мы отображаем их значения на экран и выводим все те значение, которые тут мы определили. Если же мы попытаемся получить доступ к переменной, которую не создавали, вот как на строке 24, то эта строчка кода становится ошибкой. На этой строчке интерпретатор прекращает интерпретировать все, что мы определили в сценарии и код находящийся ниже просто прекращает работать. Поэтому 24 строчка и все строчки кода, которые мы могли бы написать ниже, они не перестанут работать. Давайте проверим. Вот первая переменная undefined, потом обе переменные «b» и «c» со значением 777, переменная «d» – null. Последняя переменная – она у нас отсутствует. Если мы чтобы проверить, что действительно весь код перестает работать, если мы этот код поставим в начало, интерпретатор первое дело, что сделает – это выполнит эту строку, получит здесь ошибку и не сможет выполнять код идущий далее. Давайте запустим. Видите, в таком случае у нас вообще ничего не отображается. Поэтому следите за использованием переменных, не используйте переменные если вы их не определяли. То есть если мы хотя бы создали переменную, как только мы ее с помощью ключевого слова var определили, эта переменная остается, тип этой переменной undefined, значение этой переменной undefined. Дальше мы эту переменную можем спокойно использовать. А вот если вы пытаетесь читать переменные, которых нету, вот к чему это приводит. Приводит к ошибке.
Теперь давайте посмотрим, что такое область видимости. В следующем примере мы разберем с вами понятие глобальной и локальной области видимости. В языке JavaScript есть только две области видимости. Глобальная – это область видимости, где работают все наши сценарии. И локальная область видимости. Это область видимости, отдельной функции. Если вы работали в языке C# или С++ у вас есть глобальная область видимости и локальная область видимости, которая определяется функцией, которая определяется условием, циклом. В языке JavaScript таких вложенных областей видимости просто не существует. Либо у нас глобальная область видимости, либо область видимости функции. Если мы собираемся создать переменную глобальную, которая будет доступна абсолютно всем сценариям текущего приложения, текущего документа, нам нужно просто создать переменную в теге <script>. Если мы создаем var global, эта переменная становится глобальной, потому что она не находится внутри функции. Если мы определяем функцию и создаем переменную с ключевым словом var внутри функции, то эта переменная будет доступна только в теле этой функции. Получается глобальная область видимости, вот она, вот то что мы нарисуем сейчас красным цветом – это и есть глобальная область видимости. А локальная область видимости это вот эта вот часть, только то, что находится в функции. Глобальная область видимости, если мы создаем переменную, вот здесь, global, если мы ниже создадим еще один тег скрипт, то тег скрипт будет представлять глобальную область видимости и в новом скрипте переменная global тоже будет существовать. То есть глобальные переменные – они общие. Они доступны даже тем JavaScript сценариям, которые разрабатывали другие разработчики. Если этот JavaScript сценарий подключить к вашей странице, то чужой JavaScript код может видеть ваши глобальные переменные. То есть почему они глобальные? Потому что доступны абсолютно всем. А вот локальные переменные, они доступны только в той области, в которой мы их создавали. Область видимости создается вот фигурными скобками, операторными скобками, все, что между этими скобками находится – это одна область видимости. Эта область видимости можем использовать свои локальные переменные и так же эта область видимости так как она находится внутри глобальной области видимости, эта область видимости может получать доступ также и к глобальным переменным. Вот сейчас мы на 20 строке увидим, что переменная global у нас доступна в функции F. Мы увидим здесь текс глобальная переменная. И также мы увидим, что доступна локальная переменная, на 21 строке мы увидим текст локальная переменная. Функцию мы определили, на строке 26 функцию мы эту запускаем, то она начинает у нас работать, создает локальную переменную, выводит все эти сообщения. После запрещения функции мы отображаем значение глобальной переменной. Мы увидим, что и здесь переменная была доступна и здесь переменная доступна у нас, видно текст глобальная переменная, но вот на 31 строчке отображение к локальной переменной уже ни к чему не приводит. Локальная переменная здесь неопределенна, локальна переменная доступна только в этой области видимости, то есть вот этой локальной области видимости. Поэтому на строке №31 мы не имеем права обращаться к переменной local, ее просто здесь не существует. Если бы переменную local отсюда подняли выше и поставили ее после глобальной, тогда переменная local, она по сути имела глобальную область видимости. А в этом случае, вот в таком варианте использования как на 31 строке, это у нас обращение по сути к несуществующей переменной. Давайте запустим и проверим. Глобальная и локальная переменная. То, что выводится функцией, видите, цвет navy, вот у нас отобразились данные, а потом, когда мы попытались вывести значения global и local за функции, то первая строчка отобразилась, а вторая строчка стала причиною ошибки, потому что переменная неопределенна. Переменную мы не смогли запустить.
В следующем примере показано, что произойдет если в глобальной области видимости находится переменная, которая называется точно так же, как и переменная заложена в локальную область видимости. На 15 строке создается глобальная переменная с именем global, на строке 18 еще одна переменная с именем local, но эта переменная глобальная, потому что вы видите, она определена в теге <script>, не внутри функции, а вот в скрипте. В самой функции F на строке 23 создается еще одна переменная с именем local. Имена этих переменных совпадают, но пока будет работать функция F, значение этой переменной временно будет перекрывать значение глобальной переменной, поэтому на строке 26, когда мы обращаемся к переменной с именем local, мы обращаемся к данной переменной, а когда мы закончим работу в этой функций, когда мы обратимся к переменной local на 34 строчке, то по сути мы будем работать с переменной, которая находится на 18 строке, будем выводить это значение. Получается, что внутри функции мы можем перекрывать значение глобальных переменных, если создадим локальную переменную с тем же именем, что и у глобальной. Вот видите глобальна переменная локальная переменная, а потом после выхода за функцию вот у нас есть глобальная и еще одна глобальная переменная. То есть на строке 34 мы на самом деле обращаемся к значению, которое устанавливалось на строке 18. А на строке 26 мы обращаемся к значению, которое устанавливалось на 23 строке. Вот это такая вот особенность работы с локальными и глобальными функциями.
В №4 примере показана ошибка, которую очень часто могут допускать разработчики. Всегда создавая переменную используйте ключевое слово var. Потому что если вы не установите ключевое слово var, это может привесит к следующему поведению. Вот на строке 19 мы определяем функцию. На 22 строчки создаем переменную с именем global, но не используя ключевое слово var. Если мы не устанавливаем ключевое слово var, это означает, что мы создаем глобальную переменную. Получается, что здесь эта переменная доступна как внутри этой функции, так как она глобальная, так и за пределами этой функции. На строке 35 мы имеем доступ к переменной global. Вот когда эта функция запускается – переменная global создается, а потом, когда на 35 строке мы к этой переменной обращаемся, мы видим ее результат – глобальная переменная, точнее ее значение. Видите, глобальная переменная вот вывелась на строке 24 и вторая строчка это вывелось на строке 35. Давайте проверим что произойдет если мы раскомментируем 30 строку, раскомментируем и запустим. Видите, браузер не отобразил никаких сообщений. Почему так произошло? Функцию на 19 строке мы определили, но код, который там находится еще не срабатывал. Далее на 30 строке мы обращаемся document.write, выводим в тело документа сообщение, и это значение, которое мы собираемся отобразить, это переменная global. Переменная global не была определена, переменная global создается только тогда, когда выполняется код заложенный в функции. А вы помните, чтобы код, находящийся в функции, запустился, выполнился, функцию необходимо запустить. Запускаем мы функцию на 32 строчке, то есть вот здесь, после того, как выведем значение глобальная переменная. Поэтому на строке 30 из-за отсутствия переменной у нас происходит ошибка, интерпретатор не в состоянии выполнять код, соответственно не выполняются ни это, ни следующие строчки кода, которые здесь заложены. Поэтому здесь у нас все прекращает работу. Если мы функцию перенесем на строку, например, 17, если мы ее вызовем раньше, то в таком случае у нас создается переменная, мы сможем с этой переменной работать на 30 и на 35 строке. Либо другой вариант, мы можем просто розкоментировать 16 строку и создать глобальную переменную на 16 строчке, вывести ее на 30 строке, а потом, когда мы запустим функцию, на строке 22 мы просто переопределим значение глобальной переменной, перепишем это значение, которое было создано здесь. Из-за того, что код, который мы создаем з глобальными переменными внутри функции получается такой вот неоднозначный и запутанный. Старайтесь всегда создавая переменную функции использовать ключевое слово var, потому что таким образом вы четко говорите, что данная переменная – локальная, она работает только в этой функции. Соответственно никаких проблем с понимаем к какой переменной вы сейчас обратитесь к локальной или глобальной никаких проблем не возникает. Возьмите себе за правило. Создавая переменную всегда используйте ключевое слово var.
Теперь следующий пример. Локальных областей видимости у нас может быть несколько, то есть каждая функция выступает своей собственной локальной областью видимости и из одной локальной области переменная не может быть получена в другой локальной области. Вот в примере, который мы сейчас открыли, у нас есть две функции, функция «а» и функция «b», эти функции мы на 38-39 строке вызываем. В функции «а» у нас создается локальная переменная с именем «а» и ее значение отображается на экран. В функции «b» локальная переменная «b», и тоже значение выводится на экран. Сейчас у нас есть по сути три области видимости в этом примере. Есть первая область видимости – глобальная, вот эта вот область видимости, вторая область видимости – это вот эта локальная функция «а» и область видимости локальная для функции «b». Между этими областями видимости мы не можем иметь никакого взаимодействия, то есть если расскоментировать эту строчку, в переменную «b» в локальной области видимости функции «а» мы не получим. То есть здесь мы можем работать только с теми переменными, которые были созданы в области видимости функции «а». И тоже самое касается с областью видимости функции «b», тут мы можем работать только с теми переменными, которые были созданы здесь, в этой области видимости. Ну а глобальная область видимости она и на то глобальная, то есть то что было создано в глобальной области видимости будет доступно и для функции «b» и для функции «а». Если мы так код запустим – он срабатывает, обе переменные выводится и если мы рассоментируем обращение к переменной «b», которое не существует в переменной «а», в функции «а», у нас выведется вот 21 строчка код сработает, а потом выводится исключение для интерпретатора и на 25 строке интерпретатор перестанет работать и перестанет выполнять все инструкции, которые заложены в нашем коде.
Следующий пример еще объясняет дополнительно работу области видимости. Я думаю, что многие из вас, кто работал с каким-нибудь другим языком программирования, увидев этот пример подумает, что язык JavaScript очень странный и непонятный язык. Мы работая с другими языками программирования привыкли, что если мы создаем условия, если мы создаем цикл, какую-то конструкцию, если в этом допустим цикле мы создаем переменную, то переменная доступна только для цикла. За пределами цикла переменная использоваться уже не будет. Но из-за того, то в JavaScript есть только две области видимости – глобальная и локальная – область видимости при создании цикла она у нас не появляется просто, вот из-за этого она у нас есть такие достаточно интересные поведение в коде. Когда на 14 строке создается цикл, мы указываем, что в цикле есть счетчик, изначальное значение его ноль, цикл будет работать до тех пор, пока счетчик меньше трех и на каждой итерации будет просто увеличивать счетчик. Вот все, что делает цикл. В большинстве языков программирования счетчик, который мы создали в цикле будет доступен только вот в этой части кода. То есть вот в той области видимости, в которой была создана вот данными фигурными скобками. На самом деле, когда на 14 строке мы создаем вот эту переменную, мы создаем ее по сути, как глобальную переменную, потому что мы не находимся сейчас в функции. Переменная counter становится глобальной, она доступна как к этому циклу, так и всем другим сценариям, которые есть в этом приложении. Поэтому на 19 строке, когда мы еще раз выводим сообщение, обращаемся к counter, у нас все корректно срабатывает. Переменная counter доступна даже после того, как цикл завершил свою работу. Видите, 0,1,2 это то, что выводилось в счетчике, в цикле точнее, а настройке 19 вот желтым цветом вывелся блок с счетчиком, и мы видим счетчик у нас продолжает работать, мы имеем доступ. Для тех, кто перешел в JavaScript с другого языка программирования, это достаточно такое странное поведение. Но я думаю, что вы привыкнете к такому использованию областей видимости.
И №7 пример также показывает особенности работы областей видимости в функциях, локальных областей видимости. По сути, код, который мы сейчас видим, это по сути тот же код, который и в №6 примере, но мы создали функцию в которую поместили цикл и вывод значение счетчика. И сейчас мы видим, что на строке 16 мы создали счетчик, мы так же проверили счетчик, использовали его для создания цикла, но когда мы закончили цикл, на 21 строке счетчик все-равно для нас доступен. Если в предыдущем примере мы создали счетчик как глобальную переменную, то сейчас мы создали счетчик как локальную переменную и получается, что все так же виден вот в этой области видимости. Если мы его создали где-нибудь в функции, он доступен во все этой функции. В других языках программирования вот эти фигурные скобочки, они бы тоже считались бы областью видимости, у нас бы еще появилась область видимости для цикла for, но это не пройдет в JavaScript коде, в JavaScript коде область видимости только одна, область видимости функции, поэтому создав переменную, эта переменная будет доступна в любой части функции. И кстати очень такой важный пример, который мы не показываем в этих примерах, но вы можете столкнутся с таким кодом, вы можете написать его и не понимать, почему у вас код работает неправильно. Если вы создаете переменную, вот, например, var «а» вот здесь, в конце, допустим создаете и устанавливаете для этой переменной значение 1. Переменную «а» вы можете начать использовать в самом начале. alert «а». Она будет работать, потому что интерпретатор в JavaScript коде работает по следующему принципу. Когда интерпретатор видит, что у вас есть функция, первое, что делает интерпретатор – это проходится по всей функции, собирает все переменные, которые тут находятся. То есть еще до того, как запустится код в функции, интерпретатор будет знать, что у вас есть переменная counter и переменная «а». Когда интерпретатор соберет информацию об этих переменных, он изначально создаст их, переменные будут со значением undefined. И вот зная о переменных, интерпретатор начнет выполнять уже код. На строке 15 первое, что сделает этот интерпретатор выполнит alert и выведет значение переменной «а». Но так как переменная «а» у нас будет инициализирована только на 22 строке, а интерпретатор уже знает о ее существовании, то сейчас на 15 строке мы увидим значение undefined. А вот здесь, после инициализации, мы уже увидим значение 1. Видите undefined, выполнится цикл и значение 1. Поэтому будьте осторожны при объявлении переменных. Очень часто JavaScript разработчики всегда эти переменные определять вначале функции. Вот есть даже такой шаблон. Вы пишете ключевое слово var, потом определяете первую переменную, потом вторую переменную, допустим, третью переменную, определяете все переменные вначале, а потом уже используете их в оставшееся части функции. Вот можете для себя пользоваться таким же правилом. Объявили переменные, возможно присвоили этим переменным стартовые значения, а потом уже начинайте этими переменными пользоваться. Это будет хороший способ обезопасить себя от этих ошибок, которые мы увидели с вами при определении переменной ближе к концу функции. На этом мы завершаем вторую часть урока, которая посвящена областям видимости.
Третья часть урока, которая посвящена областям видимости. Тереть часть урока – дополнительные варианты использование функции. Начнем мы с вами третью часть с такого понятия как рекурсия или рекурсивный вызов. Рекурсия или самовызов – это запуск функции из самой же функции. Вот на 13 строке определяется функция с именем F. Эта функция принимает параметр counter. На 15 строке counter уменьшается на 1, потом выводится сообщение какое-то значение counter вот сейчас в этой функции. На строке 19 делается проверка. Если counter не равен нулю, то в таком случае мы еще раз запуская функцию F, передавая counter, который был уменьшен. То есть со строки 20 мы прыгаем на строку 13 и повторно запускаем этот же код, уменьшаем counter, выводим сообщение и делаем проверку – если counter еще не равен нулю, повторно запускаем сами себя. То есть вот это и есть сама рекурсия, рекурсивный вызов, когда функция запускает сама себя. Первая часть запуска функции будет выводить вот это сообщение, но когда у нас на 19 строке условие у нас не сработает, и мы не попадем в строку 20, не сможем запустить сами себя, то тогда все функции, которые мы запускали, они начнут выводить вот это сообщение, значение счетчика, которое было после запуска. Сейчас мы попробуем это графически отобразить, чтобы вы понимали, как это все работает. Видите, вначале срабатывала первая часть функции, вот 17 строчка – счетчик был 2, 1, 0. А потом вторая часть функции, на 22 строчке вывела счетчика 0, 1, 2. Более подробно рекурсивный вызов рассматривается на курсе C# Starter, так как это основы программирования, но мы сейчас с вами тоже потратим немножко времени для того, чтобы понять как именно работает этот код, почему у нас так странно отображаются сообщения, почему вначале выводится 2, 1, 0, потом 0, 1, 2. Давайте сейчас представим, что функция, которую мы сейчас запускаем вот это наша функция, вот то что сейчас нарисую это есть функция F. В первой части функции мы счетчик уменьшаем, ведь изначально, когда на 25 строке мы запускаем функцию, мы передаем в счетчик значение три. Вот получается, что если сюда в counter попало значение три, то на 15 строчке счетчик уменьшается до двойки и это значение мы отображаем. На строчке 19, вот в этой вот функции мы делаем проверку counter не равен нулю. Так как counter сейчас равен 2, то на 20 строке мы запускаем сами себя же. То есть вот здесь у нас идет условие и в этом условии мы запускаем свою же функцию. Запускаем еще одну функцию, передаем в эту функцию счетчик, который вот здесь равен 2, в этой функции счетчик уже будет равен 1, потому что на 15 строке мы его уменьшим, счетчик равен 1, мы этот счетчик выводим, а потом на 19 строке делаем проверку, счетчик не ноль – да, о не ноль, вот здесь значение 1, поэтому вот здесь в условии, в этой копии функции мы условие проходим, попадаем в само условие и еще раз вызываем сами себя, вызываем еще такую же функцию, передавая в эту функцию счетчик со значение 1. То есть в этой локальной области видимости у функции в счетчике будет значение ноль, потому что на строке 15 эта функция счетчик уменьшает. Раз у этой функции значение ноль, мы это значение выведем, на 19 строке условие уже не срабатывает, то есть мы не можем вызвать функцию еще раз, потому что счетчик равен нулю, мы это условие проходим и на 20 строке выводим сообщение уже другим цветом, желтым цветом выводим значение счетчика ноль. Но как вы видели в предыдущих примерах, когда мы функцию запустили, мы перешли в тело функции, выполнили тело функции и вернулись обратно в ту часть кода, где мы функцию запускали. Вот вы видите, мы функцию выполнили, теперь нам нужно перейти обратно и завершить те предыдущие функции которые у нас остались. Как только вот здесь мы до конца выполняем вот эту функцию, мы возвращаемся вот туда, в точку вызова, где в предыдущую функцию и выводим желтым цветом значение счетчика, которое было в этой функции, то есть вот 22 строку, которая выполняется для этой копии функции. Выводим значение 1, уже желтым цветом. Когда эта функции до конца выполняется, она возвращается в ту часть кода, где была вызвана, возвращается в эту функцию, и эта функция соответственно тоже выводит желтым цветом значение 2, значение своего счетчика. Вот и получается, что у нас при рекурсивном вызове в нас вначале вывелось 2, потом 1, потом 0. Потом эта функция не сможет рекурсивно запустить саму себя еще раз, поэтому вывела нолик, который во второй части функции, вернулась в предыдущую функцию – вывела 1, в предыдущей функции вывела 2. Поэтому у нас получилось 2, 1, 0 и 0, 1, 2. Вот так работает рекурсивный вызов. Для чего может использоваться рекурсивный вызов?
В примере №2 мы посмотрим, как можно с помощью саморекурсии посчитать факториал. На строке 7 мы попросим пользователя ввести значение, на строке 8 конвертируем это значение в целочисленное, потом у нас идет функция, сейчас мы разберем, что она делает, а на 18 строке мы выводим факториал вот этого числа, знак восклицания, вы знаете как записывается факториал цифрой и знак восклицания, а потом результат, который возвращает функция, факториал с параметром input. То есть факториал – это произведение нескольких чисел. Если мы скажем 4! – это означает, что нам нужно 1*2*3*4 и вот этот результат, результат умножения, это и будет факториалом. Как мы можем делать вот так, чтобы сюда мы передаем значение 4, а в результат получаем 1*2*3*4, то есть произведение вот всех этих натуральных чисел. Мы можем создать функцию факториал, которая будет саморекурсивно работать. Когда мы запускаем функцию, первое что она проверяет – значение переменной «х», то есть параметра, который сюда попал. Если параметр меньше или равен 1, то мы возвращаем 1. Но если это условие не срабатывает, на строке 15 мы берем «х» и умножаем «х» на вызов этой же функции, факториал, но с уменьшенным значением «х» *1. Поэтому, когда мы сюда передаем значение 4, мы умножаем 4 на результат умножения 4-1, в свою очередь, в той функции, которую мы запустим будет результат умножения 3-1, а во вложенной функции 2-1. То есть в итоге у нас получится, что из-за этого рекурсивного вызова мы получим каждый раз факториал возвращает 1, потом результат умножения 2 на 1, потом результат умножения 3*1 и результат умножения 4 на все предыдущие результаты. И в итоге вот это рекурсивный вызов даст нам факториал указанного числа. Давайте попробуем. Введем число 4, Факториал 4 – 24. Введем значение 20, факториал числа будет вот таким большим значением.
Следующая интересная возможность, которая встречается в JavaScript – это создания функций-литералов. Так как функция – это тип данных, мы можем спокойно создать переменную и в эту переменную присвоить функцию. На 13 строке создается переменная с именем F. И в эту переменную, вместо обычного значение, которое мы привыкли присваивать, записывается функция. Вот это и есть функция-литерал, потому что мы создали по сути значение, которым инициализируем переменную. Тип переменной F – function. Если вы вспомните самый первый наш урок, на слайде мы разбирали, что один из типов данных – это функция. Вот мы видим, что действительно можем создать переменную типа функция. Вот мы сейчас это видим на 13 строке. Функция, которую мы присваиваем переменной, мы не устанавливаем этой функции имя, то есть по сути эта функция – это анонимная функция безымянная функция, мы ее не можем запустить просто так, само по себе, потому что мы не знаем, как она называется. Но когда эта функция находится в переменной F. По сути имя функции это есть имя переменной и на строке 17, когда мы обращаемся к переменной F, так как в переменной находится функция, чтобы эту функцию запустить, нам нужно установить круглые скобки после переменной. Вот у нас по сути запуск функции, не важно, как она была определена, либо как отдельное объявление функции, либо функциональный литерал, запуск функции всегда выглядит одинаково. Вот мы запустили функцию, которая выведет сообщение Hello. На строке 20 мы переменную “f” взяли и переприсвоили ей значение, записали в переменную новую функцию. Старую функцию, которая хранится в переменной мы выбросили, а новая функция теперь выводит сообщение bye. Проверяем на 24 строке, вызываем функцию, видим, что здесь уже выводится сообщение bye. Вот Hello, а второй раз bye. В этом примере мы увидели, как использовать функции bye. В следующих курсах мы будем очень часто принимать эти функции, когда мы создаем обработчики на события, мы тоже будем изучать такую тему, очень часто мы используем функции литералы. Так же в JavaScript коде можно сделать, чтобы функция в себе содержала вложенную функцию. Это не является ошибкой, это даже приветствуется. На строке 13 определяется функция с именем author. На строке 15 мы вызываем функцию inner. Мы видим, что функция inner она у нас определена вот, на строке 17. Вот фигурные скобки, которыми определяется тело функции author, мы видим, что между этими скобками находится еще одно определение вложенной функции с именем inner. Эта функция выводит параграф с текста – inner-function. И вот мы эту inner function вызвали на 15 строке и ее же вызвали на 21 строке, но для того, чтобы этот код заработал, на строке 24 мы запускаем функцию author. Когда мы вызываем на 24 строке author функцию, у нас начинает выполняться 15 строка и 21 строка. И по сути функция inner, она доступна только для локального кода, для функции author. За пределами функции author к этой функции мы обращаться не будем. Поэтому это один из принципов инкапсуляции. Если вы сталкивались с этим термином, то в JavaScript мы можем прятать какую-то функциональность с помощью вот таких вложенных функций.
В следующем примере показан пример использования таких вложенных функций. На строке №13 у нас есть функция, которая будет считать гипотенузу. В эту функцию мы передаем два параметра «а» и «b», и на 15 строке внутри функции гипотенуза у нас есть функция pow, то есть power, возведение в квадрат. Эта функция принимает один параметр и возвращает как бы этот параметр умноженный сам на себя. Просто «х» в квадрате. На строке 19 мы используем библиотеку Math, точнее не библиотеку, а объект Math. С этим объектом мы еще не сталкивались, но рекомендую его посмотреть, посмотреть его содержимое, его методы. Видите, что в этом объекте есть различные функции для получение синуса, косинуса, различных констант, например, число пи и так далее. Вот мы сейчас на объекте масс вызываем функцию sqrt. sqrt– это квадратный корень. Вот мы вызываем квадратный корень, а в квадратный корень, точнее в параметр, передаем значение квадрат «a», и квадрат «b». Вот мы вызываем локально эту функцию, которая доступна только нам, получаем квадрат «a», квадрат «b», и возвращаем результат в виде квадратного корня. Этот квадратный корень мы выводим на 22 строке. Вот мы вывели результат.
И в последнем примере мы разберем то как можно еще использовать функции. Так как функция – это тип данных, то мы без проблем можем передать функцию в качестве аргумента другой функции. Посмотрите сейчас на текущий пример. На строке 14 создается переменная с именем add и в эту переменную записывается функция, принимающая два параметра. На 18 строке – переменная sub, функция, которая тоже принимает два параметра, но эта функция у нас выполняет вычитание, а на 15 строчке – сложение. На 24 строке у нас есть функция show, которая принимает три аргумента. Первый аргумент имеет имя CallBack Function. Если вы встречаете такую фразу как CallBack, вот вообще термин CallBack, это функция обратного вызова. Это означает, что сюда, в функцию show, когда мы ее вызываем мы в эту функцию в качестве параметра должны передать еще одну функцию, CallBack функцию. И эта CallBack функция в какой-то определенный момент времени будет запущена. То есть функция show сама решит, когда нужно запустить CallBack Function. Запустит этот CallBack Function и по сути на оповестит. Можете воспринимать CallBack Function как допустим вы запускаете функцию show, и передаете функции show свой номер телефона, чтобы функция show, когда она что-то там надумает, смогла позвонить и оповестить о каких-то изменениях. CallBack Function – это функция, которую нужно запустить для того, чтобы оповестить наш код о каких-то изменениях. Можете воспринимать для простоты вот так эту функцию. Значит мы запускаем show, передаем три параметра – CallBack, «а» и «b». И смотрите, что мы делаем на 26 строке. Мы подразумеваем, что CallBack Function она умеет выполоть какие-то действия над двумя переменными. Выполнять какие-то действия, получить результат и потом с этим результатом что-то делать. Вводить его каким-то красивым способом на страницу, отправлять куда-то на сервер, то есть делать какие-то с ним операции. Вот получается, что на 26 строке мы запускаем CallBack Function, которая первым параметром была передана, в эту функцию передаем значение, которые были получены здесь в качестве аргументов «a» и «b». То, что функция возвращает мы записываем в переменную result, а потом что-то с этой переменной делаем. Выводим в документ или еще какие-то операции делаем. То есть задача функции show, получать на вход функцию, которая будет производить вычисления, получать на вход параметры, над какими нужно производить вычисления, запустить функцию, передать в эту функцию параметры, получить результат и что-то сделать с результатом. И вот строка 32, мы просим функция show, покажи то что произойдет, если взять функцию add, взять значение 10 и 20. То есть функции show мы сейчас передаем add метод, вот эту функцию, бросаем функцию show, бросаем в функцию show значение 10, значение 20. И в результате видим, что у нас сумма двух этих значений отображается в параметре. Строка 34, просим, чтобы функция show взяла функцию sub, провела вычитания, 50-30 и результат вывела в документ. Вот таким способом мы отображаем результат. Суть этого примера показать, что вы можете спокойно делать функции аргументами других функций. Вы можете функцию передать в качестве аргумента. А другая функция может воспользоваться переданной функцией, что-то с ней здесь, вызвать ее, посмотреть на результат, выполнить любые операции. В будущем вы еще не раз столкнетесь с CallBack функциями, особенно если будете работать с различными библиотеками, например, с JQuery библиотекой, moottolls, вы будете постоянно передавать CallBack функции в чужой код, чтобы чужой код вам выдавал оповещение, чтобы чужой код вызывая вашу CallBack функцию оповещал вас о каких-то изменениях. Но с этим мы еще с вами поговорим отдельно.
На этом мы заканчиваем №4 урок, посвященный функциям, в этом уроке мы рассмотрели, как определяются функции, мы разобрали с вами различные области видимости, которые существуют в JavaScript коде, узнали, что есть глобальная и локальная область видимости и посмотрели несколько примеров, которые показали нам что такое саморекурсия, что такое функция, литералы и мы увидели, что функция – это тип данных и соответственно функцию мы можем передавать в качестве параметра в друге функции. На этом урок, посвященный функциям мы заканчиваем. Спасибо за внимание. До новых встреч.
Передача функций в компоненты – React
Как передать обработчик события (например, onClick) компоненту?
Передавайте обработчики событий и другие функции через пропсы дочерним компонентам:
<button onClick={this.handleClick}>
Если вы хотите иметь доступ к компоненту-родителю через обработчик, вам нужно привязать функцию к экземпляру компонента (см. ниже).
Как привязать функцию к экземпляру компонента?
В зависимости от того, какой синтаксис и подход к созданию компонентов вы используете, существует несколько способов удостовериться, что функции имеют доступ к таким атрибутам компонента, как this.props
и this.state
.
Привязка в конструкторе (ES2015)
class Foo extends Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log('По кнопке кликнули');
}
render() {
return <button onClick={this.handleClick}>Нажми на меня</button>;
}
}
Привязка в свойствах класса (предложение-кандидат)
class Foo extends Component {
handleClick = () => {
console.log('По кнопке кликнули');
}
render() {
return <button onClick={this.handleClick}>Нажми на меня</button>;
}
}
Привязка в методе render()
class Foo extends Component {
handleClick() {
console.log('По кнопке кликнули');
}
render() {
return <button onClick={this.handleClick.bind(this)}>Нажми на меня</button>;
}
}
Примечание:
Использование
Function.prototype.bind
в render() создаёт новую функцию при каждом рендере компонента, что может повлиять на производительность (см. ниже).
Стрелочная функция в render()
class Foo extends Component {
handleClick() {
console.log('По кнопке кликнули');
}
render() {
return <button onClick={() => this.handleClick()}>Нажми на меня</button>;
}
}
Примечание:
Использование стрелочной функции в render() создаёт новую функцию при каждой отрисовке компонента, что может нарушать оптимизации, использующие строгое сравнение для определения идентичности.
Можно ли использовать стрелочные функции в методе render()?
В целом, да. Зачастую это самый простой способ передать параметры через колбэки.
Если же у вас возникли проблемы с производительностью — оптимизируйте!
Зачем вообще нужна привязка?
В JavaScript эти два фрагмента кода не равнозначны:
var method = obj.method;
method();
Привязка гарантирует, что второй фрагмент будет работать так же, как и первый.
В React, как правило, привязывать нужно только те методы, которые вы хотите передать другим компонентам. Например, <button onClick={this.handleClick}>
передаёт this.handleClick
, поэтому его нужно привязать. Впрочем, метод render
и методы жизненного цикла привязывать не обязательно, так как мы не передаём их в другие компоненты.
Ознакомьтесь со статьёй Йехуды Катц, в которой более подробно объяснено, что такое привязка, и как работают функции в JavaScript.
Почему моя функция вызывается каждый раз при отрисовке компонента?
Убедитесь, что вы не вызываете функцию, когда передаёте её компоненту:
render() {
return <button onClick={this.handleClick()}>Нажми на меня</button>
}
Вместо этого передайте саму функцию (без скобок):
render() {
return <button onClick={this.handleClick}>Нажми на меня</button>
}
Как передать параметры обработчику событий или колбэку?
Чтобы передать параметры обработчику событий, оберните его в стрелочную функцию:
<button onClick={() => this.handleClick(id)} />
Это действие равносильно использованию .bind
:
<button onClick={this.handleClick.bind(this, id)} />
Пример: Передача параметров с использованием стрелочных функций
const A = 65
class Alphabet extends React.Component {
constructor(props) {
super(props);
this.state = {
justClicked: null,
letters: Array.from({length: 26}, (_, i) => String.fromCharCode(A + i))
};
}
handleClick(letter) {
this.setState({ justClicked: letter });
}
render() {
return (
<div>
Just clicked: {this.state.justClicked}
<ul>
{this.state.letters.map(letter =>
<li key={letter} onClick={() => this.handleClick(letter)}>
{letter}
</li>
)}
</ul>
</div>
)
}
}
Пример: Передача параметров с использованием атрибутов данных
В качестве альтернативного подхода вы можете использовать DOM API, чтобы хранить необходимые для обработчиков событий данные. Рассмотрите этот подход, если вам нужно оптимизировать большое количество элементов или использовать дерево визуализации, полагающееся на компонент React.PureComponent для проверки на равенство.
const A = 65
class Alphabet extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
this.state = {
justClicked: null,
letters: Array.from({length: 26}, (_, i) => String.fromCharCode(A + i))
};
}
handleClick(e) {
this.setState({
justClicked: e.target.dataset.letter
});
}
render() {
return (
<div>
Just clicked: {this.state.justClicked}
<ul>
{this.state.letters.map(letter =>
<li key={letter} data-letter={letter} onClick={this.handleClick}>
{letter}
</li>
)}
</ul>
</div>
)
}
}
Как предотвратить слишком быстрый или слишком частый вызов функции?
Если вы используете обработчики событий, такие как onClick
или onScroll
, и хотите предотвратить быстрое срабатывание колбэков, вы можете ограничить скорость выполнения колбэка. Для этого вы можете использовать:
- троттлинг: выборочные изменения, зависимые от частоты, основанной на времени (напр.
_.throttle
) - дебаунсинг: изменения, задействованные после некого периода бездействия (напр.
_.debounce
) - троттлинг с помощью
requestAnimationFrame
: выборочные изменения, основанные наrequestAnimationFrame
(напр.raf-schd
)
Взгляните на визуализацию, которая сравнивает функции throttle
и debounce
.
Примечание:
_.debounce
,_.throttle
иraf-schd
предусматривают методcancel
для отмены отложенных колбэков. Вы должны либо вызвать этот метод из componentWillUnmount, либо удостовериться, что компонент всё ещё встроен в пределах отложенной функции.
Throttle
Троттлинг предотвращает повторный вызов функции в заданный период времени. Этот метод был задействован в примере ниже, чтобы не допустить вызов обработчика «click» чаще чем раз в секунду.
import throttle from 'lodash.throttle';
class LoadMoreButton extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
this.handleClickThrottled = throttle(this.handleClick, 1000);
}
componentWillUnmount() {
this.handleClickThrottled.cancel();
}
render() {
return <button onClick={this.handleClickThrottled}>Загрузить ещё</button>;
}
handleClick() {
this.props.loadMore();
}
}
Debounce
Дебаунсинг гарантирует, что функция не будет выполняться до тех пор, пока не пройдёт определённое количество времени с момента её последнего вызова. Этот метод пригодится, если вам нужно провести ресурсоёмкий расчёт в ответ на событие, которое может быстро повториться (например, прокрутка страницы или нажатие клавиш). В примере ниже для ввода текста используется задержка в 250 мс.
import debounce from 'lodash.debounce';
class Searchbox extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.emitChangeDebounced = debounce(this.emitChange, 250);
}
componentWillUnmount() {
this.emitChangeDebounced.cancel();
}
render() {
return (
<input
type="text"
onChange={this.handleChange}
placeholder="Поиск..."
defaultValue={this.props.value}
/>
);
}
handleChange(e) {
this.emitChangeDebounced(e.target.value);
}
emitChange(value) {
this.props.onChange(value);
}
}
requestAnimationFrame
throttling
requestAnimationFrame
— это способ организации очереди функции, которая будет выполнена в браузере за оптимальное время для производительности отрисовки. Функция, поставленная в очередь с помощью requestAnimationFrame
, запустится в следующем кадре. Браузер приложит все усилия, чтобы обеспечить 60 кадров в секунду (60 fps — frames per second). Однако, если браузер не в состоянии справиться с этой задачей, он естественным образом ограничит количество кадров в секунду. Например, если ваше устройство поддерживает только 30 fps, то и получите вы только 30 кадров. Использование requestAnimationFrame
для троттлинга является очень полезным методом, так как помогает предотвратить выполнение более 60 обновлений в секунду. Если вы выполняете 100 обновлений в секунду, это создаёт лишнюю работу для браузера, которую пользователь всё равно не заметит.
Примечание:
Использование этой техники захватит только последнее опубликованное значение в кадре. Пример работы данной оптимизации вы можете увидеть на
MDN
import rafSchedule from 'raf-schd';
class ScrollListener extends React.Component {
constructor(props) {
super(props);
this.handleScroll = this.handleScroll.bind(this);
this.scheduleUpdate = rafSchedule(
point => this.props.onScroll(point)
);
}
handleScroll(e) {
this.scheduleUpdate({ x: e.clientX, y: e.clientY });
}
componentWillUnmount() {
this.scheduleUpdate.cancel();
}
render() {
return (
<div
style={{ overflow: 'scroll' }}
onScroll={this.handleScroll}
>
<img src="/my-huge-image.jpg" />
</div>
);
}
}
Тестирование ограничения скорости
Когда вы тестируете, что ваш код ограничения скорости работает правильно, полезно иметь возможность прокрутить время. Если вы используете jest
, вам может пригодиться mock timers
. Если вы используете requestAnimationFrame
, то raf-stub
может оказаться полезным инструментом для управления смены кадров анимации.
Частичное применение или partial application в JavaScript
Частичное применение c замыканием (closure)
Частичное применение — это применение к функции некоторых аргументов и возврат новой функции, в ожидании остальных аргументов. Примененные аргументы хранятся в замыкании (closure) и остаются доступными для любых из возвращенных функций в будущем которые частично примененны. Если ты не знаком с замыканиями, позволь рассказать тебе о них.
В JavaScript мы можем использовать области видимости способом, который позволит нам предоставить состояние функции или объекту, не подвергая это состояние непосредственно внешнему миру. Вот этот процесс и называется замыканием. Например:
function сallCount(fn) {
let count = 0
return (...args) => {
console.log(`This function has been called ${count++} times`)
fn(...args)
}
}
const add = (x, y) => x + y
const addCount = сallCount(add)
addCount(2, 4)
addCount(7, 5)
addCount(2, 4)
Функция сallCount
создает переменную count
, которая ограничена областью действия тела функции. Мы не можем получить к ней доступ извне этой функции. Однако, анонимная функция, которую мы возвращаем, использует count
; так что значение останется доступным для возвращенной функции, пока она существует. Таким образом, новая функция addCount
способна выводить состояние count
при каждом вызове.
Частичное применение c карринга
Для лучшего понимания частичного применения, советую ознакомится с карри-функциями.
Аргументы, передаваемые функции карри, позволяют нам хранить данные в закрытом виде (closure) для их повторного использования в наших приложениях. Поскольку каждый аргумент, кроме последнего, возвращает новую функцию, мы можем легко создавать повторно-используемые функции, предварительно предоставив некоторые аргументы.
Создаём карри-функцию для извлечения запросов из API, которая использует частичное применение (partial application) для создания функциональности многократного использования. Функция getFromAPI
получит baseURL
, endpoint
и функцию обратного вызова cb
, которая выполнится как только мы получим наши данные из запроса.
Используем fetch
для получения данных. Наш URL будет комбинацией baseURL
и endpoint
. Далее превратим наш ответ в JSON
. После этого вызываем обратный вызов cb
, передав наши данные data
, и “ловим” через catch
любые ошибки, если такие возникнут.
const getFromAPI = baseURL => endpoint => cb;
fetch(`${baseURL}${endpoint}`)
.then(res => res.json())
.then(data => cb(data))
.catch(err => console.error(err.message));
А сейчас давай частично применим baseURL
. Одним из общедоступных API, который мы можем использовать, является GitHub API. У нас есть частично-применённая функция getGitHub
, в которую мы можем передавать разные endpoint
. Создаем функции для запроса пользователей и репозиторий.
const getFromAPI = baseURL => endpoint => cb;
fetch(`${baseURL}${endpoint}`)
.then(res => res.json())
.then(data => cb(data))
.catch(err => {
console.error(err.message);
});
const getGithub = getFromAPI("https://api.github.com");
const getGithubUsers = getGithub("/users");
const getGithubRepos = getGithub("/repositories");
Теперь у нас есть две новые функции, каждая из которых частично применила один и тот же baseURL
, но, так же частично применила и разные endpoint
. Последнее, что мы можем передать — это обратный вызов cb
, который вызовет fetch
. Давай используем функцию getGithubUsers
и передадим различные обратные вызовы для каждого из них.
В первом вызове выведем в консоль user.login
имена. Основным преимуществом частичного применения является наша способность откладывать оценку функции, в то же время предоставляя некоторые аргументы для хранения и повторного использования этой функции в нашем приложении. Во втором вызове выведем в консоль user.avatar_url
.
const getFromAPI = baseURL => endpoint => cb =>
fetch(`${baseURL}${endpoint}`)
.then(res => res.json())
.then(data => cb(data))
.catch(err => {
console.error(err.message);
});
const getGithub = getFromAPI("https://api.github.com");
const getGithubUsers = getGithub("/users");
const getGithubRepos = getGithub("/repositories");
getGithubUsers(data => {
console.log('login', data.map(user => user.login));
});
getGithubUsers(data => {
console.log('avatar_url', data.map(user => user.avatar_url));
});
В качестве результата мы получили 2 массива login
и avatar_url
, которые содержат запрашиваемые нами данные.
Частичное применение с bind
Частичное применение можно осуществить и без карри.
Метод bind
функции позволяет нам предоставлять аргументы функции, которые применяются к новой возвращаемой функции, документация здесь.
Первый аргумент bind
— это thisArg
. Этот аргумент будет связан с this
для новой функции. Мы передадим null
, потому как не хотим, чтобы что-либо связывалось с this
.
Любой аргумент, передаваемый в bind
после thisArg
, частично применяется как аргумент к функции. Мы можем передать любое количество аргументов, и если передадим меньше, чем ожидает функция, получим частично-применённую функцию.
const multiply = (x, y) => x * y
const multiply2 = multiply.bind(null, 2)
console.log(multiply2(3))
console.log(multiply2(4))
console.log(multiply2(5))
Заключение
Частичное применение — это просто естественная комбинация каррированных функций и замыканий. Ранее примененные аргументы сохраняются и доступны для возвращаемых новых функций. Этот процесс может существенно облегчить написание и понимание кода.
Функции высшего порядка — Выразительный Javascript
Цу-ли и Цу-су похвалялись размерами своих новых программ. «Двести тысяч строк»,- сказал Цу-ли,- «не считая комментариев!» Цу-су ответил: «Пф-ф, моя – почти миллион строк». Мастер Юнь-Ма сказал: «Моя лучшая программа занимает пятьсот строк». Услышав это, Цу-ли и Цу-су испытали просветление.
Мастер Юнь-Ма, Книга программирования
Есть два способа построения программ: сделать их настолько простыми, что там очевидно не будет ошибок, или же настолько сложными, что там не будет очевидных ошибок.
Энтони Хоар, 1980 лекция на вручении премии Тьюринга
Большая программа – затратная программа, и не только из-за времени её написания. Большой размер обычно означает сложность, а сложность сбивает с толку программистов. Сбитые с толку программисты делают ошибки в программах. Большая программа означает, что багам есть где спрятаться, и их получается труднее отыскать.
Вернёмся ненадолго к двум примерам из введения. Первый самодостаточен и занимает шесть строк.
var total = 0, count = 1;
while (count <= 10) {
total += count;
count += 1;
}
console.log(total);
Второй основан на двух внешних функциях и занимает одну строку.
console.log(sum(range(1, 10)));
В каком из них скорее встретится ошибка?
Если мы добавим размер определений sum и range, вторая программа тоже получится большой – больше первой. Но я всё равно утверждаю, что она скорее всего будет правильной.
Это будет потому, что выражение решения непосредственно относится к решаемой задаче. Суммирование числового промежутка – это не циклы и счётчики. Это суммы и промежутки.
Определения этого словаря (функции sum и range) будут включать циклы, счётчики и другие случайные детали. Но потому, что они выражают более простые концепции, чем вся программа, их проще сделать правильно.
В программном контексте эти «словарные» определения часто называют абстракциями. Абстракции прячут детали и дают нам возможность разговаривать о задачах на высшем, или более абстрактном, уровне.
Сравните два рецепта горохового супа:
Добавьте в ёмкость по одной чашке сухого гороха на порцию. Добавьте воды так, чтобы она покрыла горох. Оставьте его так минимум на 12 часов. Выньте горох из воды и поместите его в кастрюлю. Добавьте 4 чашки воды на порцию. Закройте кастрюлю и тушите горох два часа. Возьмите по половине луковицы на порцию. Порежьте на куски ножом, добавьте к гороху. Возьмите по одному стеблю сельдерея на порцию. Порежьте на куски ножом, добавьте к гороху. Возьмите по морковке на порцию. Порежьте на куски ножом, добавьте к гороху. Готовьте ещё 10 минут.
Второй рецепт:
На порцию: 1 чашка сухого гороха, половина луковицы, стебель сельдерея, морковка. Вымачивайте горох 12 часов. Тушите 2 часа в 4 чашках воды на порцию. Порежьте и добавьте овощи. Готовьте ещё 10 минут.
Второй – короче и проще. Но вам нужно знать чуть больше понятий, связанных с готовкой – вымачивание, тушение, резка (и овощи).
Программируя, мы не можем рассчитывать на то, что все необходимые слова будут в нашем словаре. Из-за этого вы можете скатиться до шаблона первого рецепта: диктовать компьютеру все мелкие шажки друг за другом, не замечая понятий более высокого уровня, которые они выражают.
Второй натурой программиста должно стать умение замечать, когда понятие умоляет придумать для него новое слово и вынести в абстракцию.
Простые функции, которые мы использовали раньше, хороши для построения абстракций. Но иногда их бывает недостаточно.
В предыдущей главе мы несколько раз встречали такой цикл:
var array = [1, 2, 3];
for (var i = 0; i < array.length; i++) {
var current = array[i];
console.log(current);
}
Код пытается сказать: «для каждого элемента в массиве – вывести его в консоль». Но он использует обходной путь – с переменной для подсчёта i, проверкой длины массива, и объявлением дополнительной переменной current. Мало того, что он не очень красив, он ещё и является почвой для потенциальных ошибок. Мы можем случайно повторно использовать переменную i, вместо length написать lenght, перепутать переменные i и current, и т.п.
Давайте абстрагируем его в функцию. Можете придумать способ сделать это?
Довольно просто написать функцию, обходящую массив и вызывающую для каждого элемента console.log
function logEach(array) {
for (var i = 0; i < array.length; i++)
console.log(array[i]);
}
Но что, если нам надо делать что-то другое, нежели выводить элементы в консоль? Поскольку «делать что-то» можно представить как функцию, а функции – это просто переменные, мы можем передать это действие как аргумент:
function forEach(array, action) {
for (var i = 0; i < array.length; i++)
action(array[i]);
}
forEach(["Тили", "Мили", "Трямдия"], console.log);
Часто можно не передавать заранее определённую функцию в forEach, а создавать функцию прямо на месте.
var numbers = [1, 2, 3, 4, 5], sum = 0;
forEach(numbers, function(number) {
sum += number;
});
console.log(sum);
Выглядит похоже на классический цикл for, с телом цикла, записанным в блоке. Однако, теперь тело находится внутри функции, и также внутри скобок вызова forEach. Поэтому его нужно закрыть как фигурной, так и круглой скобкой.
Используя этот шаблон, мы можем задать имя переменной для текущего элемента массива (number), без необходимости выбирать его из массива вручную.
Вообще, нам даже не нужно писать самим forEach. Это стандартный метод массивов. Так как массив уже передан в качестве переменной, над которой мы работаем, forEach принимает только один аргумент – функцию, которую нужно выполнить для каждого элемента.
Для демонстрации удобства этого подхода вернёмся к функции из предыдущей главы. Она содержит два цикла, проходящих по массивам:
function gatherCorrelations(journal) {
var phis = {};
for (var entry = 0; entry < journal.length; entry++) {
var events = journal[entry].events;
for (var i = 0; i < events.length; i++) {
var event = events[i];
if (!(event in phis))
phis[event] = phi(tableFor(event, journal));
}
}
return phis;
}
Используя forEach мы делаем запись чуть короче и гораздо чище.
function gatherCorrelations(journal) {
var phis = {};
journal.forEach(function(entry) {
entry.events.forEach(function(event) {
if (!(event in phis))
phis[event] = phi(tableFor(event, journal));
});
});
return phis;
}
Функции, оперирующие другими функциями – либо принимая их в качестве аргументов, либо возвращая их, называются функциями высшего порядка. Если вы уже поняли, что функции – это всего лишь переменные, ничего особенного в существовании таких функций нет. Термин происходит из математики, где различия между функциями и другими значениями воспринимаются более строго.
Функции высшего порядка позволяют нам абстрагировать действия, а не только значения. Они бывают разными. Например, можно сделать функцию, создающую новые функции.
function greaterThan(n) {
return function(m) { return m > n; };
}
var greaterThan10 = greaterThan(10);
console.log(greaterThan10(11));
Можно сделать функцию, меняющую другие функции.
function noisy(f) {
return function(arg) {
console.log("calling with", arg);
var val = f(arg);
console.log("called with", arg, "- got", val);
return val;
};
}
noisy(Boolean)(0);
Можно даже делать функции, создающие новые типы управления потоком выполнения программы.
function unless(test, then) {
if (!test) then();
}
function repeat(times, body) {
for (var i = 0; i < times; i++) body(i);
}
repeat(3, function(n) {
unless(n % 2, function() {
console.log(n, "is even");
});
});
Правила лексических областей видимости, которые мы обсуждали в главе 3, работают нам на пользу в таких случаях. В последнем примере переменная n – это аргумент внешней функции. Поскольку внутренняя функция живёт в окружении внешней, она может использовать n. Тела таких внутренних функций имеют доступ к переменным, окружающим их. Они могут играть роль блоков {}, используемых в обычных циклах и условных выражениях. Важное отличие в том, что переменные, объявленные внутри внутренних функций, не попадают в окружение внешней. И обычно это только к лучшему.
Функция noisy, объявленная ранее, которая передаёт свой аргумент в другую функцию, не совсем удобна.
function noisy(f) {
return function(arg) {
console.log("calling with", arg);
var val = f(arg);
console.log("called with", arg, "- got", val);
return val;
};
}
Если f принимает больше одного параметра, она получит только первый. Можно было бы добавить кучу аргументов к внутренней функции (arg1, arg2 и т.д.) и передать все их в f, но ведь неизвестно, какого количества нам хватит. Кроме того, функция f не могла бы корректно работать с arguments.length. Так как мы всё время передавали бы одинаковое число аргументов, было бы неизвестно, сколько аргументов нам было задано изначально.
Для таких случаев у функций в JavaScript есть метод apply. Ему передают массив (или объект в виде массива) из аргументов, а он вызывает функцию с этими аргументами.
function transparentWrapping(f) {
return function() {
return f.apply(null, arguments);
};
}
Данная функция бесполезна, но она демонстрирует интересующий нас шаблон – возвращаемая ею функция передаёт в f все полученные ею аргументы, но не более того. Происходит это при помощи передачи её собственных аргументов, хранящихся в объекте arguments, в метод apply. Первый аргумент метода apply, которому мы в данном случае присваиваем null, можно использовать для эмуляции вызова метода. Мы вернёмся к этому вопросу в следующей главе.
Функции высшего порядка, которые каким-то образом применяют функцию к элементам массива, широко распространены в JavaScript. Метод forEach – одна из самых примитивных подобных функций. В качестве методов массивов нам доступно много других вариантов функций. Для знакомства с ними давайте поиграем с ещё одним набором данных.
Несколько лет назад кто-то обследовал много архивов и сделал целую книгу по истории моей фамилии. Я открыл её, надеясь найти там рыцарей, пиратов и алхимиков… Но оказалось, что она заполнена в основном фламандскими фермерами. Для развлечения я извлёк информацию по моим непосредственным предкам и перевёл в формат, пригодный для чтения компьютером.
Файл выглядит примерно так:
[
{"name": "Emma de Milliano", "sex": "f",
"born": 1876, "died": 1956,
"father": "Petrus de Milliano",
"mother": "Sophia van Damme"},
{"name": "Carolus Haverbeke", "sex": "m",
"born": 1832, "died": 1905,
"father": "Carel Haverbeke",
"mother": "Maria van Brussel"},
… и так далее
]
Этот формат называется JSON, что означает JavaScript Object Notation (разметка объектов JavaScript). Он широко используется в хранении данных и сетевых коммуникациях.
JSON похож на JavaScript по способу записи массивов и объектов – с некоторыми ограничениями. Все имена свойств должны быть заключены в двойные кавычки, а также допускаются только простые величины – никаких вызовов функций, переменных, ничего что включало бы вычисления. Также не допускаются комментарии.
JavaScript предоставляет функции JSON.stringify и JSON.parse, которые преобразовывают данные из этого формата и в этот формат. Первая принимает значение и возвращает строчку с JSON. Вторая принимает такую строчку и возвращает значение.
var string = JSON.stringify({name: "X", born: 1980});
console.log(string);
console.log(JSON.parse(string).born);
Переменная ANCESTRY_FILE, доступная здесь, содержит JSON файл в виде строки. Давайте её раскодируем и посчитаем количество упомянутых людей.
var ancestry = JSON.parse(ANCESTRY_FILE);
console.log(ancestry.length);
Чтобы найти людей, которые были молоды в 1924 году, может пригодиться следующая функция. Она отфильтровывает элементы массива, которые не проходят проверку.
function filter(array, test) {
var passed = [];
for (var i = 0; i < array.length; i++) {
if (test(array[i]))
passed.push(array[i]);
}
return passed;
}
console.log(filter(ancestry, function(person) {
return person.born > 1900 && person.born < 1925;
}));
Используется аргумент с именем test – это функция, которая производит вычисления проверки. Она вызывается для каждого элемента, а возвращаемое ею значение определяет, попадает ли этот элемент в возвращаемый массив.
В файле оказалось три человека, которые были молоды в 1924 – дедушка, бабушка и двоюродная бабушка.
Обратите внимание, функция filter не удаляет элементы из существующего массива, а строит новый, содержащий только прошедшие проверку элементы. Это чистая функция, потому что она не портит переданный ей массив.
Как и forEach, filter – это один из стандартных методов массива. В примере мы описали такую функцию, только чтобы показать, что она делает внутри. Отныне мы будем использовать её просто:
console.log(ancestry.filter(function(person) {
return person.father == "Carel Haverbeke";
}));
Допустим, есть у нас массив объектов, представляющих людей, который был получен фильтрацией массива предков. Но нам нужен массив имён, который было бы проще прочесть.
Метод map преобразовывает массив, применяя функцию ко всем его элементам и строя новый массив из возвращаемых значений. У нового массива будет та же длина, что у входного, но его содержимое будет преобразовано в новый формат.
function map(array, transform) {
var mapped = [];
for (var i = 0; i < array.length; i++)
mapped.push(transform(array[i]));
return mapped;
}
var overNinety = ancestry.filter(function(person) {
return person.died - person.born > 90;
});
console.log(map(overNinety, function(person) {
return person.name;
}));
Что интересно, люди, которые прожили хотя бы до 90 лет – это те самые, что мы видели ранее, которые были молоды в 1920-х годах. Это как раз самое новое поколение в моих записях. Видимо, медицина серьёзно улучшилась.
Как и forEach и filter, map также является стандартным методом у массивов.
Другой популярный пример работы с массивами – получение одиночного значения на основе данных в массиве. Один пример – уже знакомое нам суммирование списка номеров. Другой – поиск человека, родившегося раньше всех.
Операция высшего порядка такого типа называется reduce (уменьшение; или иногда fold, свёртывание). Можно представить её в виде складывания массива, по одному элементу за раз. При суммировании чисел мы начинали с нуля, и для каждого элемента комбинировали его с текущей суммой при помощи сложения.
Параметры функции reduce, кроме массива – комбинирующая функция и начальное значение. Эта функция чуть менее понятная, чем filter или map, поэтому обратите на неё пристальное внимание.
function reduce(array, combine, start) {
var current = start;
for (var i = 0; i < array.length; i++)
current = combine(current, array[i]);
return current;
}
console.log(reduce([1, 2, 3, 4], function(a, b) {
return a + b;
}, 0));
Стандартный метод массивов reduce, который, конечно, работает так же, ещё более удобен. Если массив содержит хотя бы один элемент, вы можете не указывать аргумент start. Метод возьмёт в качестве стартового значения первый элемент массива и начнёт работу со второго.
Чтобы при помощи reduce найти самого древнего из известных моих предков, мы можем написать нечто вроде:
console.log(ancestry.reduce(function(min, cur) {
if (cur.born < min.born) return cur;
else return min;
}));
Как бы мы могли написать предыдущий пример (поиск человека с самой ранней датой рождения) без функций высшего порядка? На самом деле, код не такой уж и ужасный:
var min = ancestry[0];
for (var i = 1; i < ancestry.length; i++) {
var cur = ancestry[i];
if (cur.born < min.born)
min = cur;
}
console.log(min);
Чуть больше переменных, на две строчки длиннее – но пока достаточно понятный код.
Функции высшего порядка раскрывают свои возможности по-настоящему, когда вам приходится комбинировать функции. К примеру, напишем код, находящий средний возраст мужчин и женщин в наборе.
function average(array) {
function plus(a, b) { return a + b; }
return array.reduce(plus) / array.length;
}
function age(p) { return p.died - p.born; }
function male(p) { return p.sex == "m"; }
function female(p) { return p.sex == "f"; }
console.log(average(ancestry.filter(male).map(age)));
console.log(average(ancestry.filter(female).map(age)));
(Глупо, что нам приходится определять сложение как функцию plus, но операторы в JavaScript не являются значениями, поэтому их не передашь в качестве аргументов.)
Вместо того, чтобы впутывать алгоритм в большой цикл, всё распределено по концепциям, которые нас интересуют – определение пола, подсчёт возраста и усреднение чисел. Мы применяем их по очереди для получения результата.
Для написания понятного кода это прямо-таки сказочная возможность. Конечно, ясность не достаётся бесплатно.
В счастливом краю элегантного кода и красивых радуг живёт гадское чудище по имени Неэффективность.
Программа, обрабатывающая массив, красивее всего представляется в виде последовательности явно разделённых шагов, каждый из которых что-то делает с массивом и возвращает новый массив. Но наслоение всех этих промежуточных массивов стоит дорого.
Точно так же, передача функции в forEach, чтобы та прошлась по массиву за нас, удобна и проста в понимании. Но вызов функций в JavaScript обходится дороже по сравнению с циклами.
Так же обстоят дела со многими техниками, улучшающими читаемость программ. Абстракции добавляют слои между чистой работой компьютера и теми концепциями, с которыми мы работаем – и в результате компьютер делает больше работы. Это не железное правило – есть языки, которые позволяют добавлять абстракции без ухудшения эффективности, и даже в JavaScript опытный программист может найти способы писать абстрактный и быстрый код. Но это проблема встречается часто.
К счастью, большинство компьютеров безумно быстрые. Если ваш набор данных не слишком велик, или время работы должно быть всего лишь достаточно быстрым с точки зрения человека (например, делать что-то каждый раз, когда пользователь жмёт на кнопку) – тогда не имеет значения, написали вы красивое решение, которое работает половину миллисекунды, или очень оптимизированное, которое работает одну десятую миллисекунды.
Удобно примерно подсчитывать, как часто будет вызываться данный кусочек кода. Если у вас есть цикл в цикле (напрямую, или же через вызов в цикле функции, которая внутри также работает с циклом), то код будет выполнен N×M раз, где N – количество повторений внешнего цикла, а M – внутреннего. Если во внутреннем цикле есть ещё один цикл, повторяющийся P раз, тогда мы уже получим N×M×P, и так далее. Это может приводить к большим числам, и когда программа тормозит, проблему часто можно локализовать в небольшом кусочке кода, находящемся внутри самого внутреннего цикла.
Мой дед, Филиберт Хавербеке, упомянут в файле с данными. Начиная с него я могу отследить свой род в поисках самого древнего из предков, Паувелса ван Хавербеке, моего прямого предка. Теперь я хочу подсчитать, какой процент ДНК у меня от него (в теории).
Чтобы пройти от имени предка до объекта, представляющего его, мы строим объект, который сопоставляет имена и людей.
var byName = {};
ancestry.forEach(function(person) {
byName[person.name] = person;
});
console.log(byName["Philibert Haverbeke"]);
Задача – не просто найти у каждой из записей отца и посчитать, сколько шагов получается до Паувелса. В истории семьи было несколько браков между двоюродными родственниками (ну, маленькие деревни и т.д.). В связи с этим ветви семейного дерева в некоторых местах соединяются с другими, поэтому генов у меня получается больше, чем 1/2 в степени G (G – количество поколений между Паувелсом и мною). Эта формула исходит из предположения, что каждое поколение расщепляет генетический фонд надвое.
Разумно будет провести аналогию с reduce, где массив низводится до единственного значения путём последовательного комбинирования данных слева направо. Здесь нам тоже надо получить единственное число, но при этом нужно следовать линиям наследственности. А они формируют не простой список, а дерево.
Мы считаем это значение для конкретного человека, комбинируя эти значения его предков. Это можно сделать рекурсивно. Если нам нужен какой-то человек, нам надо подсчитать нужную величину для его родителей, что в свою очередь требует подсчёта её для его прародителей, и т.п. По идее нам придётся обойти бесконечное множество узлов дерева, но так как наш набор данных конечен, нам надо будет где-то остановиться. Мы просто назначим значение по умолчанию для всех людей, которых нет в нашем списке. Логично будет назначить им нулевое значение – люди, которых нет в списке, не несут в себе ДНК нужного нам предка.
Принимая данные о человеке, функцию для комбинирования значений от двух предков и значение по умолчанию, функция reduceAncestors «конденсирует» значение из семейного древа.
function reduceAncestors(person, f, defaultValue) {
function valueFor(person) {
if (person == null)
return defaultValue;
else
return f(person, valueFor(byName[person.mother]),
valueFor(byName[person.father]));
}
return valueFor(person);
}
Внутренняя функция valueFor работает с одним человеком. Благодаря рекурсивной магии она может вызвать себя для обработки отца и матери этого человека. Результаты вместе с объектом person передаются в f, которая и вычисляет нужное значение для этого человека.
Теперь мы можем использовать это для подсчёта процента ДНК, которое мой дедушка разделил с Паувелсом ванн Хавербеке, и поделить это на четыре.
function sharedDNA(person, fromMother, fromFather) {
if (person.name == "Pauwels van Haverbeke")
return 1;
else
return (fromMother + fromFather) / 2;
}
var ph = byName["Philibert Haverbeke"];
console.log(reduceAncestors(ph, sharedDNA, 0) / 4);
Человек по имени Паувелс ванн Хавербеке, очевидно, делит 100% ДНК с Паувелсом ванн Хавербеке (полных тёзок в списке данных нет), поэтому для него функция возвращает 1. Все остальные делят средний процент их родителей.
Статистически, у меня примерно 0,05% ДНК совпадает с моим предком из XVI века. Это, конечно, приблизительное число. Это довольно мало, но так как наш генетический материал составляет примерно 3 миллиарда базовых пар, во мне есть что-то от моего предка.
Можно было бы подсчитать это число и без использования reduceAncestors. Но разделение общего подхода (обход древа) и конкретного случая (подсчёт ДНК) позволяет нам писать более понятный код и использовать вновь части кода для других задач. Например, следующий код выясняет процент известных предков данного человека, доживших до 70 лет.
function countAncestors(person, test) {
function combine(person, fromMother, fromFather) {
var thisOneCounts = test(person);
return fromMother + fromFather + (thisOneCounts ? 1 : 0);
}
return reduceAncestors(person, combine, 0);
}
function longLivingPercentage(person) {
var all = countAncestors(person, function(person) {
return true;
});
var longLiving = countAncestors(person, function(person) {
return (person.died - person.born) >= 70;
});
return longLiving / all;
}
console.log(longLivingPercentage(byName["Emile Haverbeke"]));
Не нужно относиться к таким расчётам слишком серьёзно, так как наш набор содержит произвольную выборку людей. Но код демонстрирует, что reduceAncestors – полезная часть общего словаря для работы со структурой данных типа фамильного древа.
Метод bind, который есть у всех функций, создаёт новую функцию, которая вызовет оригинальную, но с некоторыми фиксированными аргументами.
Следующий пример показывает, как это работает. В нём мы определяем функцию isInSet, которая говорит, есть ли имя человека в заданном наборе. Для вызова filter мы можем либо написать выражение с функцией, которое вызывает isInSet, передавая ей набор строк в качестве первого аргумента, или применить функцию isInSet частично.
var theSet = ["Carel Haverbeke", "Maria van Brussel",
"Donald Duck"];
function isInSet(set, person) {
return set.indexOf(person.name) > -1;
}
console.log(ancestry.filter(function(person) {
return isInSet(theSet, person);
}));
console.log(ancestry.filter(isInSet.bind(null, theSet)));
Вызов bind возвращает функцию, которая вызовет isInSet с первым аргументом theSet, и последующими аргументами такими же, какие были переданы в bind.
Первый аргумент, который сейчас установлен в null, используется для вызовов методов – так же, как было в apply. Мы поговорим об этом позже.
Возможность передавать вызов функции другим функциям – не просто игрушка, но очень полезное свойство JavaScript. Мы можем писать выражения «с пробелами» в них, которые затем будут заполнены при помощи значений, возвращаемых функциями.
У массивов есть несколько полезных методов высшего порядка – forEach, чтобы сделать что-то с каждым элементом, filter – чтобы построить новый массив, где некоторые значения отфильтрованы, map – чтобы построить новый массив, каждый элемент которого пропущен через функцию, reduce – для комбинации всех элементов массива в одно значение.
У функций есть метод apply для передачи им аргументов в виде массива. Также у них есть метод bind для создания копии функции с частично заданными аргументами.
Используйте метод reduce в комбинации с concat для свёртки массива массивов в один массив, у которого есть все элементы входных массивов.
var arrays = [[1, 2, 3], [4, 5], [6]];
Разница в возрасте матерей и их детей
Используя набор данных из примера, подсчитайте среднюю разницу в возрасте между матерями и их детьми (это возраст матери во время появления ребёнка). Можно использовать функцию average, приведённую в главе.
Обратите внимание – не все матери, упомянутые в наборе, присутствуют в нём. Здесь может пригодиться объект byName, который упрощает процедуру поиска объекта человека по имени.
function average(array) {
function plus(a, b) { return a + b; }
return array.reduce(plus) / array.length;
}
var byName = {};
ancestry.forEach(function(person) {
byName[person.name] = person;
});
Историческая ожидаемая продолжительность жизни
Мы считали, что только последнее поколение людей дожило до 90 лет. Давайте рассмотрим этот феномен поподробнее. Подсчитайте средний возраст людей для каждого из столетий. Назначаем столетию людей, беря их год смерти, деля его на 100 и округляя: Math.ceil(person.died / 100).
function average(array) {
function plus(a, b) { return a + b; }
return array.reduce(plus) / array.length;
}
В качестве призовой игры напишите функцию groupBy, абстрагирующую операцию группировки. Она должна принимать массив и функцию, которая вычисляет группу для элементов массива, и возвращать объект, который сопоставляет названия групп массивам членов этих групп.
У массивов есть ещё стандартные методы every и some. Они принимают как аргумент некую функцию, которая, будучи вызванной с элементом массива в качестве аргумента, возвращает true или false. Так же, как && возвращает true, только если выражения с обеих сторон оператора возвращают true, метод every возвращает true, когда функция возвращает true для всех элементов массива. Соответственно, some возвращает true, когда заданная функция возвращает true при работе хотя бы с одним из элементов массива. Они не обрабатывают больше элементов, чем необходимо – например, если some получает true для первого элемента, он не обрабатывает оставшиеся.
Напишите функции every и some, которые работают так же, как эти методы, только принимают массив в качестве аргумента.
console.log(every([NaN, NaN, NaN], isNaN));
console.log(every([NaN, NaN, 4], isNaN));
console.log(some([NaN, 3, 4], isNaN));
console.log(some([2, 3, 4], isNaN));
#5 ИНСТРУКЦИИ и встроенные функции / грамматика языка JS
Приступаем к изучению грамматике языка JavaScript. Хочу сразу отметить что уроки я делаю специально короткие, стараюсь уложиться в 7 – 15 минут, и это для того что бы вы могли легко найти нужный вам приме, воспроизвести его и разобраться с данной информацией которая тут изложена.
Курс будет большой, постараюсь раскрыть основные темы JavaScript, и я Вас уверяю, если от начала до конца его просмотрите, сможете творить реально крутые вещи, но а пока на первом этапе проходим базу, другими словами закладываем фундамент.
Инструкции в языке JavaScript
Изучение языка JS во многом схож с изучением иностранного языка, необходимо запоминать новые слова, разбираться с пунктуацией и овладевать новыми правилами. Начнем с базовой единица программирования такой как Инструкция JavaScript.
«Инструкцией» – обозначает одно действие в программе JS. Во многих случаях инструкция – это одна строка кода и если провести аналогию с русским языком, то она схожа с предложением. Если в русском языке предложения разделяются точками, то в JS мы разделяем их с точкой запетой. Точка с запятой означает, что предложение закончено, и интерпретатор JS должен перейти к следующему действию.
Общий алгоритм написания программ таков: напечатали «инструкцию», поставили точку с запятой, поставили пустую строку, напечатали следующую инструкцию, закончили ее точкой с запятой и т.д. до тех пор, пока программа не будет завершена.
Точно так же, как мы объединяем предложения, чтобы получить абзац, мы комбинируем инструкции для создания программ на языке JS.
Не всегда вы сможете увидеть в некоторых программ в конце инструкции точку запятой, официально она считается не обязательным знаком в JS, и некоторые программисты опускают ее. Но это не значит, что так нужно делать, отсутствие точки с запятой затрудняет чтение кода, а иногда может быть причиной ошибок в JS.
Изначально приучайте себя писать код правильно, закрывайте свою инструкцию точкой с запятой.
Встроенные функции
В языке JS присутствуют встроенные программы, позволяющие выполнять различные действия на веб-страницах. Такие программы называются функциями, по другому их могут называть «методами», они похожи как в русском языке на главы и предложения. Такие функции легко узнать по наличию скобок после их объявления.
Например, функция alert() которую мы задействовали в предыдущих уроках, имеет название самой функции alert, и скобки в которых передаем параметр, который обработан будет при помощи данной функции. Это функция позволяет в браузере открывать диалоговое окно с отображением сообщения. То есть, это некий заложенный алгоритм программы, вызывая его, получаем определенный результат.
Язык JS имеет множество различных функций, о которых я буду рассказывать в процессе всего курса. По мимо встроенных функций Js позволяет нам создавать собственные, поэтому диапазон операций, которые могут выполнять сценарии, шире, чем стандартный набор команд языка JS. Подробнее с ними мы познакомимся в отдельном уроке, где будем разбирать работу с функциями.
Но на этом вводный урок по грамматике JS я завершаю, в следующем видео разбирать типы данных в языке JavaScript.
Урок подготовил Горелов Денис.
Оставить комментарий:
Представлена новая атака для обхода изоляции сайтов в Chrome
Представлена новая атака для обхода изоляции сайтов в Chrome
Alexander Antipov
Spook.js – атака на базе JavaScript, направленная на обход реализованных Google механизмов защиты от уязвимостей Spectre.
Новая Spectre-подобная атака по сторонним каналам на современные процессоры позволяет успешно обходить изоляцию сайтов (функцию безопасности, реализованную в Google Chrome и других браузерах на базе Chromium) и похищать чувствительные данные.
Атака, получившая название Spook.js, была разработана группой ученых Мичиганского университета, Технологического института Джорджии (США), Университета Аделаиды (Австралия) и Тель-Авивского университета (Израиль).
Spook.js представляет собой метод атаки на базе JavaScript, специально направленный на обход механизмов защиты, реализованных Google после обнаружения уязвимостей Spectre и Meltdown в январе 2018 года. Эти механизмы предотвращают потенциальные утечки, гарантируя, что контент разных доменов не будет совместно использоваться в одном и том же адресном пространстве.
По словам исследователей, подконтрольные злоумышленникам web-страницы могут знать, какие другие страницы этого же сайта просматривает пользователь, извлекать из них чувствительную информацию и даже восстанавливать учетные данные, если они были введены с помощью функции автоматического заполнения. Если пользователь установил вредоносное расширение для Chrome, то злоумышленники могут извлекать данные и из него. Как следствие, хакеры могут выводить любые хранящиеся в памяти сайта данные или извлекать их из вредоносного расширения.
Spectre (CVE-2017-5753 и CVE-2017-5715) – название класса аппаратных уязвимостей в центральных процессорах, «ломающего» изоляцию между приложениями. Уязвимости позволяют злоумышленникам заставлять программу получать доступ к произвольным участкам, связанным с выделенной ей областью памяти, читать содержимое этих участков памяти и потенциально похищать чувствительные данные.
В июле 2018 года компания Google реализовала в Chrome программные контрмеры для защиты от атак Spectre в виде изоляции сайтов. С включенной изоляцией сайтов Chrome 67 и более поздние версии загружают каждый сайт в своем отдельном процессе и тем самым предотвращают атаки между процессами, а значит, и между сайтами.
Однако недавно специалисты обнаружили сценарии, при которых изоляция сайтов не разделяет два сайта и делает возможной Spectre-подобную атаку Spook.js, позволяющую похищать данные из Chrome и других браузеров на базе Chromium, запущенных на компьютерах с процессорами Intel, AMD и Apple M1.
«Chrome разделяет ‘example.com’ и ‘example.net’, поскольку у них разные домены верхнего уровня, то же самое происходит с ‘example.com’ и ‘attacker.com.’ Однако ‘attacker.example.com’ и ‘corporate.example.com’ позволено иметь один процесс, благодаря чему страницы сайта ‘attacker.example.com’ потенциально могут извлекать информацию из страниц сайта ‘corporate.example.com’», — сообщили исследователи.
В ответ на исследование в июле 2021 года команда безопасности
Chrome расширила изоляцию сайтов таким образом, чтобы «расширения больше не могли
иметь общие процессы». Новая настройка Strict Extension Isolation активирована в Chrome 92 и более поздних версиях.
Больше пяти не собираться: роботы будут следить за улицами Сингапура, хакеры атаковали проект Jenkins, во Франции арестовали экологов, данные которых раскрыл ProtonMail, а россиян беспокоит идея «социальных рейтингов». Смотрите 31-й выпуск наших новостей.
Поделиться новостью:
функций — JavaScript | MDN
Функции — один из основных строительных блоков JavaScript. Функция в JavaScript похожа на процедуру — набор операторов, выполняющих задачу или вычисляющих значение, но для того, чтобы процедура квалифицировалась как функция, она должна принимать некоторые входные данные и возвращать выходные данные, где существует очевидная связь между вход и выход. Чтобы использовать функцию, вы должны определить ее где-нибудь в области видимости, из которой вы хотите ее вызвать.
См. Также исчерпывающую справочную главу о функциях JavaScript, чтобы узнать подробности.
Объявления функций
Определение функции (также называемое объявлением функции или оператором функции ) состоит из ключевого слова function
, за которым следует:
- Имя функции.
- Список параметров функции, заключенный в круглые скобки и разделенный запятыми.
- Операторы JavaScript, определяющие функцию, заключенные в фигурные скобки,
{...}
.
Например, следующий код определяет простую функцию с именем квадрат
:
функция квадрат (число) {
номер возврата * номер;
}
Функция квадрат
принимает один параметр, называемый числом
.Функция состоит из одного оператора, который говорит, что нужно вернуть параметр функции (то есть число
), умноженный на самого себя. Оператор return
указывает значение, возвращаемое функцией:
Примитивные параметры (например, число) передаются в функции по значению ; значение передается в функцию, но если функция изменяет значение параметра , это изменение не отражается глобально или в вызывающей функции .
Если вы передаете объект (т.е., непримитивное значение, такое как Массив
или определенный пользователем объект) в качестве параметра, и функция изменяет свойства объекта, это изменение видно за пределами функции, как показано в следующем примере:
function myFunc (theObject) {
theObject.make = 'Тойота';
}
var mycar = {марка: 'Honda', модель: 'Accord', год: 1998};
var x, y;
x = mycar.make;
myFunc (mycar);
y = mycar.make;
Выражения функций
Хотя приведенное выше объявление функции синтаксически является оператором, функции также могут быть созданы с помощью выражения функции.
Такой функцией может быть анонимный ; у него не обязательно должно быть имя. Например, функция квадрат
могла быть определена как:
const square = функция (число) {возвращаемое число * число}
var x = квадрат (4)
Однако имя может содержать функциональное выражение. Указание имени позволяет функции ссылаться на себя, а также упрощает идентификацию функции в трассировке стека отладчика:
const factorial = функция fac (n) {return n <2? 1: n * fac (n - 1)}
консоль.журнал (факториал (3))
Функциональные выражения удобны при передаче функции в качестве аргумента другой функции. В следующем примере показана функция map
, которая должна получать функцию в качестве первого аргумента и массив в качестве второго аргумента:
функциональная карта (f, a) {
пусть результат = [];
могу я;
для (i = 0; i! = a.length; i ++)
результат [i] = f (a [i]);
вернуть результат;
}
В следующем коде функция получает функцию, определенную выражением функции, и выполняет ее для каждого элемента массива, полученного в качестве второго аргумента:
функциональная карта (f, a) {
пусть результат = [];
могу я;
для (i = 0; i! = a.длина; я ++)
результат [i] = f (a [i]);
вернуть результат;
}
const f = function (x) {
вернуть х * х * х;
}
пусть числа = [0, 1, 2, 5, 10];
пусть куб = карта (е, числа);
console.log (куб);
Функция возвращает: [0, 1, 8, 125, 1000]
.
В JavaScript функция может быть определена на основе условия. Например, следующее определение функции определяет myFunc
, только если num
равно 0
:
var myFunc;
if (num === 0) {
myFunc = function (theObject) {
объект.make = 'Toyota';
}
}
Помимо определения функций, как описано здесь, вы также можете использовать конструктор Function
для создания функций из строки во время выполнения, как и eval ()
.
Метод - это функция, которая является свойством объекта. Подробнее об объектах и методах читайте в разделе Работа с объектами.
Определение функции не означает, что выполняет ее. Его определение дает имя функции и указывает, что делать при вызове функции.
При вызове функция фактически выполняет указанные действия с указанными параметрами. Например, если вы определяете функцию квадрат
, вы можете вызвать ее следующим образом:
Предыдущий оператор вызывает функцию с аргументом 5
. Функция выполняет свои операторы и возвращает значение 25
.
Функции должны быть в области видимости , когда они вызываются, но объявление функции может быть поднято (появится под вызовом в коде), как в этом примере:
консоль.бревно (квадрат (5));
функция square (n) {return n * n}
Область видимости функции - это функция, в которой она объявлена (или вся программа, если она объявлена на верхнем уровне).
Примечание: Это работает только при определении функции с использованием синтаксиса выше (т. Е. function funcName () {}
). Приведенный ниже код работать не будет.
Это означает, что подъем функции работает только с объявлениями функции - но не с выражениями функции .
console.log (квадрат)
console.log (квадрат (5))
const square = function (n) {
вернуть n * n;
}
Аргументы функции не ограничиваются строками и числами. Вы можете передавать в функцию целые объекты. Функция showProps ()
(определенная в разделе Работа с объектами) является примером функции, которая принимает объект в качестве аргумента.
Функция может вызывать сама себя. Например, вот функция, рекурсивно вычисляющая факториалы:
function factorial (n) {
если ((n === 0) || (n === 1))
возврат 1;
еще
return (n * факториал (n - 1));
}
Затем вы можете вычислить факториалы от 1
до 5
следующим образом:
var a, b, c, d, e;
а = факториал (1);
b = факториал (2);
c = факториал (3);
d = факториал (4);
е = факториал (5);
Есть и другие способы вызова функций.Часто бывают случаи, когда функцию необходимо вызывать динамически, или количество аргументов функции меняется, или когда контекст вызова функции должен быть установлен на конкретный объект, определенный во время выполнения.
Оказывается, что функций сами являются объектами - и, в свою очередь, эти объекты имеют методы. (См. Объект Function
.) Один из них, метод apply ()
, может использоваться для достижения этой цели.
Переменные, определенные внутри функции, не могут быть доступны из любого места за пределами функции, потому что переменная определяется только в области видимости функции.Однако функция может получить доступ ко всем переменным и функциям, определенным внутри области, в которой она определена.
Другими словами, функция, определенная в глобальной области видимости, может обращаться ко всем переменным, определенным в глобальной области видимости. Функция, определенная внутри другой функции, также может обращаться ко всем переменным, определенным в ее родительской функции, и к любым другим переменным, к которым родительская функция имеет доступ.
var num1 = 20,
число2 = 3,
name = 'Чамах';
function multiply () {
return num1 * num2;
}
умножить ();
function getScore () {
var num1 = 2,
число2 = 3;
function add () {
вернуть имя + 'забил' + (num1 + num2);
}
return add ();
}
getScore ();
Рекурсия
Функция может обращаться к самой себе и вызывать ее.Функция может ссылаться на себя тремя способами:
- Имя функции
-
arguments.callee
- Переменная в области видимости, которая ссылается на функцию
Например, рассмотрим следующее определение функции:
var foo = function bar () {
}
В теле функции все следующие эквиваленты:
-
бар ()
-
arguments.callee ()
-
foo ()
Функция, которая вызывает сама себя, называется рекурсивной функцией .В некотором смысле рекурсия аналогична циклу. Оба выполняют один и тот же код несколько раз, и оба требуют условия (чтобы избежать бесконечного цикла или, скорее, бесконечной рекурсии в этом случае).
Например, следующий цикл ...
var x = 0;
while (x <10) {
x ++;
}
... можно преобразовать в объявление рекурсивной функции с последующим вызовом этой функции:
function loop (x) {
если (x> = 10)
возвращение;
петля (х + 1);
}
петля (0);
Однако некоторые алгоритмы не могут быть простыми итерационными циклами.Например, получить все узлы древовидной структуры (такой как DOM) проще с помощью рекурсии:
function walkTree (node) {
если (узел == нуль)
возвращение;
for (var i = 0; i
По сравнению с функцией цикла
, каждый рекурсивный вызов сам по себе делает здесь много рекурсивных вызовов.
Можно преобразовать любой рекурсивный алгоритм в нерекурсивный, но логика часто намного сложнее, и для этого требуется использование стека.
Фактически, сама рекурсия использует стек: стек функций. Поведение в виде стека можно увидеть в следующем примере:
function foo (i) {
если (я <0)
возвращение;
console.log ('начало:' + я);
foo (я - 1);
console.log ('конец:' + я);
}
foo (3);
Вложенные функции и замыкания
Вы можете вложить функцию в другую функцию. Вложенная (внутренняя) функция является частной для своей содержащей (внешней) функции.
Он также образует крышку .Замыкание - это выражение (чаще всего функция), которое может иметь свободные переменные вместе со средой, которая связывает эти переменные (которая «закрывает» выражение).
Поскольку вложенная функция является замыканием, это означает, что вложенная функция может «наследовать» аргументы и переменные содержащейся в ней функции. Другими словами, внутренняя функция содержит область действия внешней функции.
Суммируем:
- Доступ к внутренней функции можно получить только из операторов внешней функции.
- Внутренняя функция образует замыкание: внутренняя функция может использовать аргументы и переменные внешней функции, в то время как внешняя функция не может использовать аргументы и переменные внутренней функции.
В следующем примере показаны вложенные функции:
function addSquares (a, b) {
function square (x) {
вернуть х * х;
}
вернуть квадрат (а) + квадрат (б);
}
а = addSquares (2, 3);
b = addSquares (3, 4);
c = addSquares (4, 5);
Поскольку внутренняя функция образует замыкание, вы можете вызвать внешнюю функцию и указать аргументы как для внешней, так и для внутренней функции:
функция снаружи (х) {
function inside (y) {
вернуть x + y;
}
вернуться внутрь;
}
fn_inside = снаружи (3);
результат = fn_inside (5);
результат1 = снаружи (3) (5);
Сохранение переменных
Обратите внимание, как x
сохраняется, когда возвращается внутри
.Замыкание должно сохранять аргументы и переменные во всех областях, на которые оно ссылается. Поскольку каждый вызов предоставляет потенциально разные аргументы, для каждого вызова за пределами
создается новое закрытие. Память может быть освобождена только тогда, когда возвращенный внутри
больше не доступен.
Это не отличается от хранения ссылок в других объектах, но часто менее очевидно, потому что ссылки не устанавливаются напрямую и невозможно их проверить.
Множественно-вложенные функции
Функции могут быть множественно-вложенными.Например:
- Функция (
A
) содержит функцию (B
), которая сама содержит функцию (C
). - Обе функции
B
иC
здесь образуют закрытие. Итак,B
может получить доступ кA
, аC
может получить доступ кB
. - Кроме того, поскольку
C
может получить доступ кB
, который может получить доступ кA
,C
также может получить доступ кA
.
Таким образом, укупорочные средства могут содержать несколько областей видимости; они рекурсивно содержат объем функций, содержащих его.Это называется цепочкой осциллографов . (Причина, по которой это называется «сцеплением», объясняется позже.)
Рассмотрим следующий пример:
функция A (x) {
функция B (y) {
функция C (z) {
console.log (x + y + z);
}
С (3);
}
БИ 2);
}
А (1);
В этом примере C
обращается к B
y
и A
x
.
Это можно сделать, потому что:
-
B
образует укупорочное средство, включаяA
(т.е.например,B
может получить доступ к аргументам и переменнымA
). -
C
образует укупорочное средство, включаяB
. - Поскольку закрытие
B
включаетA
, закрытиеC
включаетA
,C
может получить доступ к какB,
иA
аргументам и переменным. Другими словами,C
связывают с областямиB
иA
, в таком порядке .
Обратное, однако, неверно. A
не может получить доступ к C
, потому что A
не может получить доступ к любому аргументу или переменной B
, переменной которой является C
. Таким образом, C
остается частным только для B
.
Конфликты имен
Когда два аргумента или переменных в области действия замыкания имеют одинаковое имя, возникает конфликт имен . Более вложенные области имеют приоритет. Таким образом, самая внутренняя область видимости имеет наивысший приоритет, а самая внешняя область - наименьший.Это цепочка областей видимости. Первая в цепочке - это самая внутренняя область видимости, а последняя - самая внешняя. Рассмотрим следующее:
функция за пределами () {
var x = 5;
function inside (x) {
вернуть x * 2;
}
вернуться внутрь;
}
снаружи () (10);
Конфликт имен происходит при выполнении инструкции return x * 2
и находится между внутри параметра
x
и вне переменной
x
. Цепочка областей видимости здесь: { внутри
, вне
, глобальный объект}.Следовательно, внутри
x
имеет приоритет над вне
x
, а 20
( внутри
x
) возвращается вместо 10
( за пределами
). х
).
Замыкания - одна из самых мощных функций JavaScript. JavaScript позволяет вложение функций и предоставляет внутренней функции полный доступ ко всем переменным и функциям, определенным внутри внешней функции (и ко всем другим переменным и функциям, к которым внешняя функция имеет доступ).
Однако внешняя функция не имеет доступа и к переменным и функциям, определенным внутри внутренней функции. Это обеспечивает своего рода инкапсуляцию для переменных внутренней функции.
Кроме того, поскольку внутренняя функция имеет доступ к области действия внешней функции, переменные и функции, определенные во внешней функции, будут жить дольше, чем продолжительность выполнения внешней функции, если внутренней функции удастся выжить после срока службы внешняя функция.Замыкание создается, когда внутренняя функция каким-то образом становится доступной для любой области вне внешней функции.
var pet = function (name) {
var getName = function () {
возвращаемое имя;
}
return getName;
}
myPet = pet ('Виви');
мой питомец();
Это может быть намного сложнее, чем приведенный выше код. Может быть возвращен объект, содержащий методы для управления внутренними переменными внешней функции.
var createPet = function (name) {
var sex;
возвращение {
setName: function (newName) {
name = newName;
},
getName: function () {
возвращаемое имя;
},
getSex: function () {
ответный секс;
},
setSex: function (newSex) {
if (typeof newSex === 'строка' && (newSex.toLowerCase () === 'мужской' ||
newSex.toLowerCase () === 'female')) {
sex = newSex;
}
}
}
}
var pet = createPet ('Виви');
pet.getName ();
домашний питомец.setName ('Оливер');
pet.setSex ('мужчина');
pet.getSex ();
pet.getName ();
В приведенном выше коде переменная name
внешней функции доступна для внутренних функций, и нет другого способа получить доступ к внутренним переменным, кроме как через внутренние функции. Внутренние переменные внутренних функций действуют как безопасные хранилища для внешних аргументов и переменных. Они содержат «постоянные» и «инкапсулированные» данные, с которыми могут работать внутренние функции.Функции даже не обязательно должны быть присвоены переменной или иметь имя.
var getCode = (function () {
var apiCode = '0] Eal (eh & 2';
return function () {
return apiCode;
};
}) ();
получить код();
Примечание: При использовании замыканий следует остерегаться ряда ловушек!
Если закрытая функция определяет переменную с тем же именем, что и переменная во внешней области видимости, то нет возможности снова сослаться на переменную во внешней области.(Переменная внутренней области видимости "переопределяет" внешнюю, пока программа не выйдет из внутренней области видимости.)
var createPet = function (name) {
возвращение {
setName: function (name) {
name = name;
}
}
}
Аргументы функции хранятся в объекте, подобном массиву. Внутри функции вы можете адресовать переданные ей аргументы следующим образом:
, где i
- порядковый номер аргумента, начиная с 0
.Итак, первым аргументом, переданным функции, будет arguments [0]
. Общее количество аргументов указано аргументами. Длина
.
Используя объект arguments
, вы можете вызвать функцию с большим количеством аргументов, чем официально объявлено для принятия. Это часто бывает полезно, если вы заранее не знаете, сколько аргументов будет передано функции. Вы можете использовать arguments.length
, чтобы определить количество аргументов, фактически переданных функции, а затем получить доступ к каждому аргументу с помощью объекта arguments
.
Например, рассмотрим функцию, которая объединяет несколько строк. Единственным формальным аргументом функции является строка, в которой указаны символы, разделяющие элементы для объединения. Функция определяется следующим образом:
функция myConcat (разделитель) {
var result = '';
var i;
for (i = 1; i
Этой функции можно передать любое количество аргументов, и она объединит каждый аргумент в строку «список»:
myConcat (',', 'красный', 'оранжевый', 'синий');
myConcat (';', 'слон', 'жираф', 'лев', 'гепард');
myConcat ('.',' шалфей ',' базилик ',' душица ',' перец ',' петрушка ');
Примечание: Переменная arguments
«подобна массиву», но не массиву. Он похож на массив в том смысле, что имеет пронумерованный индекс и свойство длины . Однако не обладает всеми методами работы с массивами.
См. Объект Function
в справке по JavaScript для получения дополнительной информации.
Начиная с ECMAScript 2015, есть два новых типа параметров: параметры по умолчанию и остальные параметры .
Параметры по умолчанию
В JavaScript параметры функций по умолчанию undefined
. Однако в некоторых ситуациях может быть полезно установить другое значение по умолчанию. Именно это и делают параметры по умолчанию.
Без параметров по умолчанию (до ECMAScript 2015)
В прошлом общая стратегия установки значений по умолчанию заключалась в проверке значений параметров в теле функции и присвоении значения, если они равны undefined
.
В следующем примере, если для b
не указано значение, его значение будет undefined
при вычислении a * b
, а вызов умножить
обычно вернул бы NaN
.Однако этому препятствует вторая строка в этом примере:
.
функция multiply (a, b) {
b = typeof b! == 'undefined'? б: 1;
вернуть a * b;
}
умножить (5);
С параметрами по умолчанию (после ECMAScript 2015)
С параметрами по умолчанию ручная проверка в теле функции больше не требуется. Вы можете указать 1
как значение по умолчанию для b
в заголовке функции:
функция умножения (a, b = 1) {
вернуть a * b;
}
умножить (5);
Подробнее см. Параметры по умолчанию в справочнике.
Остальные параметры
Синтаксис остальных параметров позволяет нам представить неопределенное количество аргументов в виде массива.
В следующем примере функция умножить
использует остальных параметров для сбора аргументов от второго до конца. Затем функция умножает их на первый аргумент.
функция multiply (multiplier, ... theArgs) {
вернуть theArgs.map (x => multiplier * x);
}
var arr = multiply (2, 1, 2, 3);
консоль.журнал (обр);
Выражение стрелочной функции (ранее, а теперь неверно известное как жирная стрелочная функция ) имеет более короткий синтаксис по сравнению с функциональными выражениями и не имеет собственного this
, arguments, super или new.target. Стрелочные функции всегда анонимны. См. Также сообщение в блоге hacks.mozilla.org: «Подробное описание ES6: стрелочные функции».
На введение стрелочных функций повлияли два фактора: более короткие функции и необязательные из это
.
Более короткие функции
В некоторых функциональных шаблонах приветствуются более короткие функции. Сравнить:
var a = [
«Водород»,
'Гелий',
"Литий",
'Бериллий'
];
var a2 = a.map (функция (и) {return s.length;});
console.log (a2);
var a3 = a.map (s => s.length);
console.log (a3);
Нет отдельного
this
До стрелочных функций каждая новая функция определяла свое собственное значение this
(новый объект в случае конструктора, не определен в вызовах функций в строгом режиме, базовый объект, если функция вызывается как «объектный метод» и т. д.). Это оказалось далеко не идеальным для объектно-ориентированного стиля программирования.
function Person () {
this.age = 0;
setInterval (function growUp () {
this.age ++;
}, 1000);
}
var p = новый человек ();
В ECMAScript 3/5 эта проблема была исправлена путем присвоения значения в и
переменной, которую можно было закрыть.
function Person () {
var self = this;
self.age = 0;
setInterval (function growUp () {
себя.age ++;
}, 1000);
}
В качестве альтернативы может быть создана связанная функция, чтобы правильное значение и это значение
передавалось в функцию growUp ()
.
Стрелочная функция не имеет собственного , это
; - это значение
окружающего контекста выполнения. Таким образом, в следующем коде , это
в функции, которая передается в setInterval
, имеет то же значение, что и , это
во включающей функции:
function Person () {
это.возраст = 0;
setInterval (() => {
this.age ++;
}, 1000);
}
var p = новый человек ();
JavaScript имеет несколько встроенных функций верхнего уровня:
-
eval ()
Метод
eval ()
оценивает код JavaScript, представленный в виде строки.-
неравномерное ()
Метод
uneval ()
создает строковое представление исходного кода объектаObject
.-
isFinite ()
Глобальная функция
isFinite ()
определяет, является ли переданное значение конечным числом. При необходимости параметр сначала преобразуется в число.-
isNaN ()
Функция
isNaN ()
определяет, равно ли значениеNaN
или нет. Примечание: принуждение внутри функцииisNaN
имеет интересные правила; в качестве альтернативы вы можете использовать номер.isNaN ()
, как определено в ECMAScript 2015, или вы можете использоватьtypeof
, чтобы определить, является ли значение Not-A-Number.-
parseFloat ()
Функция
parseFloat ()
анализирует строковый аргумент и возвращает число с плавающей запятой.-
parseInt ()
Функция
parseInt ()
анализирует строковый аргумент и возвращает целое число указанного основания (основание в математических системах счисления).-
decodeURI ()
Функция
decodeURI (),
декодирует унифицированный идентификатор ресурса (URI), ранее созданныйencodeURI
или аналогичной программой.-
decodeURIComponent ()
Метод
decodeURIComponent ()
декодирует компонент унифицированного идентификатора ресурса (URI), ранее созданныйencodeURIComponent
или аналогичной процедурой.-
encodeURI ()
Метод
encodeURI ()
кодирует унифицированный идентификатор ресурса (URI), заменяя каждый экземпляр определенных символов одной, двумя, тремя или четырьмя escape-последовательностями, представляющими кодировку символа UTF-8 (будет только четыре escape-последовательности для символов, состоящих из двух «суррогатных» символов).-
encodeURIComponent ()
Метод
encodeURIComponent ()
кодирует компонент унифицированного идентификатора ресурса (URI), заменяя каждый экземпляр определенных символов одной, двумя, тремя или четырьмя escape-последовательностями, представляющими кодировку символа UTF-8 (будет только четыре escape-последовательности для символов, состоящих из двух «суррогатных» символов).-
побег ()
Устаревший метод
escape ()
вычисляет новую строку, в которой определенные символы были заменены шестнадцатеричной escape-последовательностью. Вместо этого используйтеencodeURI
илиencodeURIComponent
.-
unescape ()
Устаревший метод
unescape ()
вычисляет новую строку, в которой шестнадцатеричные escape-последовательности заменяются символом, который она представляет.Управляющие последовательности могут быть введены функцией вродеescape
. Посколькуunescape ()
устарел, используйте вместо негоdecodeURI ()
илиdecodeURIComponent
.
Компоненты и свойства - React
Компоненты позволяют разделить пользовательский интерфейс на независимые, многоразовые части и рассматривать каждую часть отдельно. Эта страница дает представление о компонентах. Вы можете найти подробную справку по API компонентов здесь.
Концептуально компоненты подобны функциям JavaScript. Они принимают произвольные входные данные (называемые «реквизитами») и возвращают элементы React, описывающие, что должно появиться на экране.
Компоненты функций и классов
Самый простой способ определить компонент - написать функцию JavaScript:
функция Добро пожаловать (реквизит) {
return Здравствуйте, {props.name}
;
}
Эта функция является допустимым компонентом React, поскольку она принимает единственный аргумент объекта «props» (который обозначает свойства) с данными и возвращает элемент React.Мы называем такие компоненты «функциональными компонентами», потому что они буквально являются функциями JavaScript.
Вы также можете использовать класс ES6 для определения компонента:
class Welcome extends React.Component {
оказывать() {
return Привет, {this.props.name}
;
}
}
Два вышеуказанных компонента эквивалентны с точки зрения React.
Компоненты
Function и Class имеют некоторые дополнительные функции, которые мы обсудим в следующих разделах.
Визуализация компонента
Раньше мы встречали только элементы React, которые представляют теги DOM:
Однако элементы также могут представлять определенные пользователем компоненты:
const element = <Добро пожаловать name = "Сара" />;
Когда React видит элемент, представляющий определенный пользователем компонент, он передает атрибуты JSX и дочерние элементы этому компоненту как единый объект. Мы называем этот объект «реквизитом».
Например, этот код отображает на странице «Привет, Сара»:
function Добро пожаловать (реквизиты) {return Привет, {реквизиты.имя}
;
}
const element = ; ReactDOM.render (
элемент,
document.getElementById ('корень')
);
Попробовать на CodePen
Давайте резюмируем, что происходит в этом примере:
- Мы вызываем
ReactDOM.render ()
с элементом - React вызывает компонент
Welcome
с{name: 'Sara'}
в качестве реквизита. - Наш компонент
Welcome
возвращает в качестве результата элемент
.Hello, Sara
- React DOM эффективно обновляет DOM для соответствия
.Hello, Sara
Примечание: Всегда начинайте имена компонентов с заглавной буквы.
React рассматривает компоненты, начинающиеся со строчных букв, как теги DOM. Например,
представляет тег HTML div, нопредставляет компонент и требует, чтобы
Welcome
находился в области действия.Чтобы узнать больше о причинах этого соглашения, прочтите JSX In Depth.
Составные компоненты
Компоненты могут ссылаться на другие компоненты в своих выводах. Это позволяет нам использовать одну и ту же абстракцию компонентов для любого уровня детализации. Кнопка, форма, диалог, экран: в приложениях React все это обычно выражается в виде компонентов.
Например, мы можем создать компонент App
, который многократно отображает Welcome
:
функция Добро пожаловать (реквизит) {
return Здравствуйте, {props.name}
;
}
function App () {
возвращение (
);
}
ReactDOM.оказывать(
<Приложение />,
document.getElementById ('корень')
);
Попробовать на CodePen
Обычно новые приложения React имеют один компонент App
на самом верху. Однако, если вы интегрируете React в существующее приложение, вы можете начать снизу вверх с небольшого компонента, такого как Button
, и постепенно продвигаться к вершине иерархии представлений.
Не бойтесь разбивать компоненты на более мелкие.
Например, рассмотрим этот компонент Comment
:
комментарий функции (реквизиты) {
возвращение (
{props.author.name}
{props.text}
{formatDate (props.date)}
);
}
Попробовать на CodePen
Он принимает автора
(объект), текст
(строка) и дата
(дата) в качестве реквизита и описывает комментарий на веб-сайте социальной сети.
Этот компонент может быть сложно изменить из-за всей вложенности, а также трудно повторно использовать отдельные его части. Выделим из него несколько составляющих.
Сначала извлечем Аватар
:
функция Аватар (реквизит) {
возвращение (
);
}
Аватар
не должен знать, что он отображается внутри комментария
.Вот почему мы дали его опоре более общее имя: пользователь
, а не автор
.
Мы рекомендуем присваивать имена реквизитам с точки зрения самого компонента, а не контекста, в котором он используется.
Теперь мы можем упростить Комментарий
немного:
комментарий функции (реквизиты) {
возвращение (
{реквизит.имя автора}
{props.text}
{formatDate (props.date)}
);
}
Затем мы извлечем компонент UserInfo
, который отображает Avatar
рядом с именем пользователя:
функция UserInfo (реквизиты) {
возвращение (
{props.user.name} );
}
Это позволяет нам еще больше упростить Комментарий
:
комментарий функции (реквизиты) {
возвращение (
{props.text}
{formatDate (props.date)}
);
}
Попробовать на CodePen
Поначалу извлечение компонентов может показаться тяжелой работой, но наличие палитры повторно используемых компонентов окупается в более крупных приложениях.Хорошее практическое правило состоит в том, что если часть вашего пользовательского интерфейса используется несколько раз ( Button
, Panel
, Avatar
) или достаточно сложна сама по себе ( App
, FeedStory
, Comment
), это хороший кандидат для выделения в отдельный компонент.
Реквизиты доступны только для чтения
Независимо от того, объявляете ли вы компонент как функцию или как класс, он никогда не должен изменять свои собственные свойства. Рассмотрим эту функцию суммы
:
function sum (a, b) {
вернуть a + b;
}
Такие функции называются «чистыми», потому что они не пытаются изменить свои входные данные и всегда возвращают один и тот же результат для одних и тех же входных данных.
Напротив, эта функция нечистая, потому что она изменяет свой собственный ввод:
функция вывода (счет, сумма) {
account.total - = сумма;
}
React довольно гибкий, но у него есть одно строгое правило:
Все компоненты React должны действовать как чистые функции по отношению к своим свойствам.
Конечно, пользовательские интерфейсы приложений динамичны и со временем меняются. В следующем разделе мы представим новое понятие «состояние». Состояние позволяет компонентам React изменять свой вывод с течением времени в ответ на действия пользователя, сетевые ответы и что-либо еще, не нарушая этого правила.
math.apply (A, dim, callback) | Примените функцию, которая отображает массив в скаляр по заданной оси матрицы или массива. |
math.column (значение, индекс) | Вернуть столбец из матрицы. |
math.concat (a, b, c,… [, dim]) | Объедините две или более матриц. |
math.count (x) | Подсчитайте количество элементов матрицы, массива или строки. |
math.cross (x, y) | Вычислите векторное произведение двух векторов в трехмерном пространстве. |
math.ctranspose (x) | Транспонировать и комплексно сопрягать матрицу. |
math.det (x) | Вычислить определитель матрицы. |
math.diag (X) | Создание диагональной матрицы или получение диагонали матрицы Если x является вектором, будет возвращена матрица с вектором x по диагонали. |
math.diff (обр.) | Создайте новую матрицу или массив разницы между элементами данного массива Необязательный параметр dim позволяет указать измерение для оценки разницы. Если параметр измерения не передан, предполагается, что измерение 0 Измерение основано на нуле в javascript и один на основе парсера и может быть числом или двоичным числом. Массивы должны быть «прямоугольными», что означает массивы типа [1, 2]. Если что-то передается как матрица, оно будет возвращено как матрица, но кроме этого все матрицы преобразуются в массивы.А. |
математический фильтр (x, тест) | Отфильтруйте элементы в массиве или одномерной матрице. |
мат. Плоский (x) | Выровнять многомерную матрицу в одномерную матрицу. |
math.forEach (x, обратный вызов) | Обходит все элементы матрицы / массива и выполняет заданную функцию обратного вызова. |
math.getMatrixDataType (x) | Найдите тип данных всех элементов в матрице или массиве, например «число», если все элементы являются числами, и «комплексный», если все значения являются комплексными числами. |
math.identity (n) | Создайте двумерную единичную матрицу размером m x n или n x n. |
math.inv (x) | Вычислить обратную квадратную матрицу. |
math.kron (x, y) | Вычисляет произведение кронекера двух матриц или векторов. |
math.map (x, обратный вызов) | Создайте новую матрицу или массив с результатами функции обратного вызова, выполняемой для каждой записи матрицы / массива. |
math.matrixFromColumns (… arr) | Создайте плотную матрицу из векторов как отдельных столбцов. |
math.matrixFromFunction (size, fn) | Создайте матрицу, оценив производящую функцию по каждому индексу. |
math.matrixFromRows (… arr) | Создайте плотную матрицу из векторов как отдельных строк. |
math.ones (m, n, p,…) | Создайте матрицу, заполненную единицами. |
math.partitionSelect (x, k) | Выбор массива или одномерной матрицы на основе разделов. |
math.range (начало, конец [, шаг]) | Создайте массив из диапазона. |
math.reshape (x, размеры) | Измените форму многомерного массива в соответствии с указанными размерами. |
math.resize (x, size [, defaultValue]) | Изменить размер матрицы. |
математ.повернуть (w, theta) | Повернуть вектор размером 1x2 против часовой стрелки на заданный угол Повернуть вектор размером 1x3 против часовой стрелки на заданный угол вокруг заданной оси. |
math.rotationMatrix (theta) | Создайте двумерную матрицу вращения против часовой стрелки (2x2) для заданного угла (выраженного в радианах). |
math.row (значение, индекс) | Вернуть строку из матрицы. |
математ.размер (x) | Вычислить размер матрицы или скаляра. |
math.sort (x) | Сортировка элементов в матрице. |
X = math.sqrtm (A) | Вычислить главный квадратный корень из квадратной матрицы. |
math.squeeze (x) | Сожмите матрицу, удалите внутренние и внешние одиночные измерения из матрицы. |
math.subset (x, index [, replace]) | Получить или установить подмножество матрицы или строки. |
math.trace (x) | Вычислить след матрицы: сумму элементов на главной диагонали квадратной матрицы. |
math.transpose (x) | Транспонировать матрицу. |
математические нули (m, n, p,…) | Создайте матрицу, заполненную нулями. |
Создание бессерверных функций с помощью JavaScript
Чтобы добавить бессерверную лямбда-функцию в свой проект, создайте файл JavaScript в каталоге сконфигурированных функций.Файл может храниться непосредственно в каталоге функций или в подкаталоге, предназначенном для функции. Конечная точка функции определяется ее именем файла или именем ее выделенного родительского каталога.
Например, с настроенным каталогом функций my_functions
и именем конечной точки целевой функции hello
вы можете сохранить файл функции одним из следующих способов:
-
my_functions / hello.js
-
мои_функции / привет / привет.js
-
my_functions / hello / index.js
Любой из вышеперечисленных форматов развернет синхронную бессерверную функцию, которая может быть вызвана на следующей конечной точке относительно базового URL-адреса вашего сайта: /.netlify/ функции / привет
.
Для фоновой функции имя файла функции должно заканчиваться на -background
, или подкаталог, содержащий файл фоновой функции, должен заканчиваться на -background
.
В примере настроенного каталога функций my_functions
и имени конечной точки целевой функции hello-background
вы можете сохранить файл фоновой функции одним из следующих способов:
-
my_functions / hello-background.js
-
my_functions / hello-background / hello-background.js
-
my_functions / hello-background / index.js
Эти форматы будут развертывать фоновую функцию, которая может быть вызвана на следующей конечной точке, относительной на базовый URL вашего сайта: /.netlify/functions/hello-background
.
Зарезервированные имена для функций, управляемых событиями
Определенные имена функций зарезервированы как триггеры, инициированные различными встроенными событиями на вашем сайте Netlify.Вы можете найти список этих имен функций и то, как они работают, на странице, посвященной функциям, управляемым событиями.
Формат синхронной функции
Каждый файл JavaScript, который будет развернут как синхронная бессерверная лямбда-функция, должен экспортировать метод обработчика со следующим общим синтаксисом:
Netlify предоставляет событие
и параметры контекста
, когда бессерверная функция вызван. Когда вы вызываете конечную точку бессерверной функции, обработчик
получает объект события
, аналогичный тому, что вы получили бы от AWS API Gateway:
Параметр context
включает информацию о контексте, в котором была вызвана бессерверная функция, например, определенная информация о пользователе Identity.
Хотя функции Netlify по-прежнему поддерживают синтаксис обратного вызова
, async
более универсален. Поскольку функции async
возвращают обещание
, мы рекомендуем возвращать ответ, по крайней мере, с кодом состояния HTTP, вместо того, чтобы допускать тайм-аут функции. Использование async
в коде бессерверной функции не означает автоматически бессерверную функцию в качестве фоновой.
Вот простой пример бессерверной функции: hello.js
:
Эта бессерверная функция будет вызываться с вашего сайта по адресу /.netlify/functions/hello
и в случае успеха вернет код состояния 200 и сообщение «Hello, World».
Синхронные бессерверные функции могут возвращать объект ответа, который включает следующую информацию:
Формат фоновой функции
Эта функция присутствует в BETA
и может быть недоступна на всех планах .
Синтаксис фоновой функции аналогичен синтаксису синхронной бессерверной функции, но обычно вы передаете результат функции не исходному клиенту, а другому адресату.Каждый файл JavaScript, который должен быть развернут в качестве фоновой функции, должен экспортировать метод обработчика . Подобно формату синхронных бессерверных функций, Netlify предоставляет параметры события
и контекста
для выполнения фоновой функции. Хотя фоновые функции полагаются на асинхронный вызов, они не требуют использования синтаксиса async
JavaScript.
Вот пример фоновой функции: send-pdf-background.js
:
Эта фоновая функция будет вызываться с вашего сайта по адресу /.Нетлифай / функции / отправить-pdf-фон
. При вызове функция добавляется в очередь с ответом об успешном завершении 202
и ожидает выполнения. При успешном выполнении создается отчет в формате PDF, который отправляется по электронной почте в качестве вложения.
Параметры времени выполнения
Netlify использует Node.js 12 в качестве среды выполнения по умолчанию для бессерверных функций, написанных на JavaScript.
Вы можете указать другую версию среды выполнения с помощью переменной среды в пользовательском интерфейсе Netlify. Например, чтобы использовать Node.js 14 для всех будущих развернутых бессерверных функций, установите для переменной AWS_LAMBDA_JS_RUNTIME
значение nodejs14.x
.
Эта переменная принимает любое допустимое имя среды выполнения AWS Lambda для JavaScript.
Развернуть для применения изменений
Переменные среды и настройки времени выполнения применяются к функциям во время развертывания. Всегда повторно развертывайте свои функции, чтобы применить новые настройки.
Развертывание разделенных функций JavaScript
Вы можете использовать непрерывное развертывание или Netlify CLI, чтобы подготовить разделенные функции JavaScript для развертывания в AWS.Бот сборки Netlify просматривает файл функции, чтобы отметить зависимости. Затем бот извлекает необходимые зависимости из связанной папки node_modules
и заархивирует их с файлом функции для развертывания.
Файл package.json
разделенной функции JavaScript не обязательно должен находиться в той же папке, что и файл функции. Чтобы оптимизировать производительность сборки, мы рекомендуем указывать зависимости функций в файле верхнего уровня package.json
в базовом каталоге сайта.Вот пример рекомендуемого макета:
Для получения дополнительных сведений о том, как работает эта функция, посетите репозиторий базового модуля: @ netlify / zip-it-and-ship-it.
Связка с esbuild
Вы можете связать свои функции JavaScript с esbuild, чтобы оптимизировать время сборки и размер артефактов. Чтобы включить эту возможность подписки на бета-версию, добавьте node_bundler = "esbuild"
в свой файл конфигурации Netlify [functions]
раздел.
Мы создали несколько инструментов, которые помогут с написанием, тестированием и развертыванием ваших бессерверных функций JavaScript на Netlify:
- Netlify Dev - Netlify CLI включает инструменты для разработки локальных функций и упрощенного развертывания с помощью смоделированной производственной среды Netlify.Команда
netlify dev
запускает сервер фреймворка, если фреймворк обнаружен, и обрабатывает перенаправления, правила прокси, функции Netlify и надстройки. - Отладка функций Netlify CLI - команда
netlify functions: serve
(в настоящее время находится в стадии бета-тестирования) позволяет моделировать функции Netlify на автономном сервере без дополнительных накладных расходов на Netlify Dev. Вы можете проверить серверный процесс функций для отладки бессерверных функций. - netlify-lambda - Мы предоставляем дополнительный инструмент сборки для функций Netlify, который больше не рекомендуется для новых проектов или распространенных вариантов использования.Репозиторий README включает сравнение Netlify Dev и netlify-lambda.
Если вам нужно проанализировать multipart / form-data
-encoded event.body
, мы рекомендуем использовать busboy. Чтобы узнать больше, посетите нашу статью в блоге об обработке данных, состоящих из нескольких частей, с помощью функций Netlify.
Поддержка TypeScript
Netlify также поддерживает бессерверные функции, созданные с помощью TypeScript. Прочтите нашу документацию по созданию с помощью TypeScript, чтобы узнать больше о их настройке.
Функции JS - это объекты
Да, в мире JS функция считается объектом, и чтобы объяснить причину, по которой нам придется изучать типы. Типы в JavaScript подразделяются на:
- Примитивы (строка, число, null, логическое значение, неопределенное значение, символ) : это неизменяемые типы данных. Они не являются объектами, не имеют методов и хранятся в памяти по значению.
- Непримитивы (функции, массивы и объекты) : это изменяемые типы данных.Это объекты, и они хранятся в памяти по ссылке.
Как видите, функции находятся внутри непримитивной категории , что означает, что при определении функции вы создаете объект.
Связанные курсы премиум-класса
Итак, давайте создадим функцию и будем относиться к ней так, как если бы мы только что создали объект.
Давайте назначим переменную и зарегистрируем ее:
function showFavoriteIceCream () {
const favIceCream = 'chocolate';
консоль.log (`Мое любимое мороженое $ {favIceCream}`);
}
showFavoriteIceCream.flavours = ['шоколад', 'ваниль', 'клубника'];
console.log (showFavoriteIceCream);
Вот что мы получаем:
# Давайте назначим функцию и запишем ее:
function showFavoriteIceCream () {
const favIceCream = 'chocolate';
console.log (`Мое любимое мороженое - $ {favIceCream}`);
}
showFavoriteIceCream.ароматизаторы = ['шоколад', 'ваниль', 'клубника'];
showFavoriteIceCream.showFlavours = function () {
return this.flavours;
};
console.log (showFavoriteIceCream);
Вот что это дает:
Как видите, функция
showFavoriteIceCream
помимо выполнения действия также ведет себя как объект, мы можем назначать ей свойства и методы.На самом деле, функции известны как первоклассных объектов (или первоклассных граждан), что означает, что они могут делать больше, чем это:
- Они могут храниться в переменных.
const showFavIceCreams = function () {};
const listOfData = ['ваниль', 5, showFavIceCreams];
const thisIsAnObject = {
showListOfIceCreams: function () {},
};
Предварительный просмотр
- Их можно передать как параметры в другой функции.
function getFavoriteIceCream () {
return 'chocolate';
}
function logFavoriteIceCream (func) {
return func ();
}
консоль.журнал (logFavoriteIceCream (getFavoriteIceCream));
Предварительный просмотр
- Они могут вернуться из другой функции.
function getFavoriteIceCream () {
const myFavIceCream = 'шоколад';
return function () {
return 'Мое любимое мороженое' + myFavIceCream;
};
}
const functionReturned = getFavoriteIceCream ();
console.log (functionReturned ());
Preview
Отлично, теперь мы можем сказать, что функции - это объекты.Теперь давайте внимательнее посмотрим на один из журналов:
Почему функции регистрируются как Function (а не как «Object» или что-то подобное)?
Ну, это потому, что функции - это не просто объекты, это объекты Function , а это означает, что помимо возможности назначать им свойства и методы, они также имеют свойства и методы, уже определенные встроенным объектом Function .
Точно так же, как массивы - это особые виды объектов, массивы также являются обычными объектами с некоторыми предопределенными методами, доступными для них.Чтобы быть точным, как для объектов типа
Array
, так и для объектов типаFunction
«специальные» методы определены на специальном прототипе.А что такое встроенный объект ? В JS есть стандартных встроенных объектов , на которых основаны другие объекты. Сказано, что стандартная встроенная функция Function является объектом, на котором основаны функции.
Встроенный объект Function имеет такие свойства, как name , length и имеет такие методы, как call , apply и bind .Давайте зарегистрируем некоторые из них, чтобы увидеть существующее доказательство:
function showFavoriteIceCream (favIceCream) {
console.log (`Мое любимое мороженое - $ {favIceCream}`);
}
console.log ('name:' + showFavoriteIceCream.name);
console.log ('длина:' + showFavoriteIceCream.length);
console.log ('вызов:' + showFavoriteIceCream.call);
console.log ('применить:' + showFavoriteIceCream.apply);
console.log ('привязка:' + showFavoriteIceCream.связывать);
Предварительный просмотр
name
дает имя, которое вы дали функции, длинапоказывает количество ожидаемых параметров.
call ()
,apply ()
иbind ()
могут помочь вам с вызовом функции - вы можете узнать больше об этих методах (в основном оbind ()
) в этом руководстве.Зная это, мы можем видеть функции в совершенно другой перспективе, и вот пример того, как мы можем увидеть это за кулисами:
Если вы новичок в JavaScript, можно легко упустить из виду, что функции являются объектами. .И это может затруднить понимание, почему вы можете делать такие вещи, как
myFunction.bind ()
. Или почему функции являются справочными значениями.Следовательно, важно понимать, что функции - это не «что-то особенное» в JavaScript, а - это просто объекты . Объекты со специальными встроенными методами и свойствами.
Эта статья написана Микджаилом Салазаром, вы можете посетить его профиль в LinkedIn.
Функции JavaScript - упражнения, практика, решение
Функции JavaScript [29 упражнений с решением]
[ Внизу страницы доступен редактор для написания и выполнения сценариев.]
1. Напишите функцию JavaScript, которая переворачивает число. Перейти в редактор
Пример x = 32243;
Ожидаемый результат: 34223
Щелкните меня, чтобы увидеть решение2. Напишите функцию JavaScript, которая проверяет, является ли переданная строка палиндромом? Перейти к редактору
Палиндром - это слово, фраза или последовательность, которые читаются так же, как вперед, назад, например, мадам или медсестры бегут.
Щелкните меня, чтобы увидеть решение3. Напишите функцию JavaScript, которая генерирует все комбинации строки. Перейти в редактор
Пример строки: 'dog'
Ожидаемый результат: d, do, dog, o, og, g
Щелкните меня, чтобы увидеть решение4. Напишите функцию JavaScript, которая возвращает переданную строку с буквами в алфавитном порядке. Перейти в редактор
Пример строки: 'webmaster'
Ожидаемый результат: 'abeemrstw'
Предположим, что символы пунктуации и чисел не включены в переданную строку.
Щелкните меня, чтобы увидеть решение5. Напишите функцию JavaScript, которая принимает строку в качестве параметра и преобразует первую букву каждого слова строки в верхний регистр. Перейти в редактор
Пример строки : 'the quick brown fox'
Ожидаемый результат: 'The Quick Brown Fox'
Щелкните меня, чтобы увидеть решение6. Напишите функцию JavaScript, которая принимает строку в качестве параметра и находит самое длинное слово в строке. Перейти в редактор
Пример строки : «Учебник по веб-разработке»
Ожидаемый результат: «Разработка»
Щелкните меня, чтобы увидеть решение7. Напишите функцию JavaScript, которая принимает строку в качестве параметра и подсчитывает количество гласных в строке. Перейти к редактору
Примечание. Поскольку буква «y» может рассматриваться как как гласная, так и как согласная, мы не считаем «y» здесь гласной.
Пример строки : 'The quick brown fox'
Ожидаемый результат: 5
Щелкните меня, чтобы увидеть решение8. Напишите функцию JavaScript, которая принимает число в качестве параметра и проверяет, является ли число простым или нет. Перейти к редактору
Примечание. Простое число (или простое число) - это натуральное число больше 1, которое не имеет положительных делителей, кроме 1 и самого себя.
Щелкните меня, чтобы увидеть решение9. Напишите функцию JavaScript, которая принимает аргумент и возвращает тип. Перейти в редактор
Примечание. Существует шесть возможных значений, которые возвращает typeof: объект, логическое значение, функция, число, строка и неопределенное значение.
Щелкните меня, чтобы увидеть решение10. Напишите функцию JavaScript, которая возвращает матрицу идентичности n строк и n столбцов. Перейти в редактор
Щелкните меня, чтобы увидеть решение11. Напишите функцию JavaScript, которая будет принимать массив сохраненных чисел и находить второе наименьшее и второе наибольшее числа соответственно. Перейти в редактор
Образец массива: [1,2,3,4,5]
Ожидаемый результат: 2,4
Щелкните меня, чтобы увидеть решение.12. Напишите функцию JavaScript, которая определяет, является ли число идеальным. Перейти к редактору
Согласно Википедии: В теории чисел совершенное число - это положительное целое число, равное сумме своих положительных делителей, то есть сумме его положительных делителей, исключая само число (также известное как как его аликвотная сумма). Точно так же совершенное число - это число, равное половине суммы всех его положительных делителей (включая само себя).
Пример : Первое совершенное число - 6, потому что 1, 2 и 3 являются его собственными положительными делителями, а 1 + 2 + 3 = 6.Точно так же число 6 равно половине суммы всех его положительных делителей: (1 + 2 + 3 + 6) / 2 = 6. Следующее совершенное число - 28 = 1 + 2 + 4 + 7 + 14. Это за которыми следуют идеальные числа 496 и 8128.
Щелкните меня, чтобы увидеть решение.13. Напишите функцию JavaScript для вычисления множителей положительного целого числа. Перейти в редактор
Щелкните меня, чтобы увидеть решение.14. Напишите функцию JavaScript для преобразования суммы в монеты. Перейти в редактор
Пример функции : amountTocoins (46, [25, 10, 5, 2, 1])
Здесь 46 - сумма. а 25, 10, 5, 2, 1 - монеты.
Выход : 25, 10, 10, 1
Щелкните меня, чтобы увидеть решение.15. Напишите функцию JavaScript для вычисления значения b n , где n - показатель степени, а b - основания. Примите b и n от пользователя и отобразите результат. Перейти в редактор
Щелкните меня, чтобы увидеть решение.16. Напишите функцию JavaScript для извлечения уникальных символов из строки. Перейти в редактор
Пример строки : "thequickbrownfoxjumpsoverthelazydog"
Ожидаемый результат : "thequickbrownfxjmpsvlazydg"
Щелкните меня, чтобы увидеть решение.17. Напишите функцию JavaScript, чтобы получить количество вхождений каждой буквы в указанной строке. Перейти в редактор
Щелкните меня, чтобы увидеть решение.18. Напишите функцию для поиска массивов JavaScript с помощью двоичного поиска. Перейти в редактор
Примечание : Бинарный поиск выполняет поиск, разбивая массив на все меньшие и меньшие части, пока не будет найдено желаемое значение.
Щелкните меня, чтобы увидеть решение.19. Напишите функцию JavaScript, которая возвращает элементы массива, размер которых превышает число. Перейти в редактор
Щелкните меня, чтобы увидеть решение.20. Напишите функцию JavaScript, которая генерирует строковый идентификатор (заданной длины) из случайных символов. Перейти в редактор
Пример списка символов : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
Щелкните меня, чтобы увидеть решение.21. Напишите функцию JavaScript, чтобы получить все возможные подмножества с комбинациями фиксированной длины (например, 2) в массиве. Перейти в редактор
Образец массива : [1, 2, 3] и длина подмножества 2
Ожидаемый результат : [[2, 1], [3, 1], [3, 2], [ 3, 2, 1]]
Щелкните меня, чтобы увидеть решение.22. Напишите функцию JavaScript, которая принимает два аргумента, строку и букву, и функция будет подсчитывать количество вхождений указанной буквы в строке. Перейти в редактор
Пример аргументов : 'w3resource.com', 'o'
Ожидаемый результат : 2
Щелкните меня, чтобы увидеть решение23. Напишите функцию JavaScript для поиска первого неповторяющегося символа. Перейти в редактор
Пример аргументов : 'abacddbec'
Ожидаемый результат : 'e'
Щелкните меня, чтобы увидеть решение24. Напишите функцию JavaScript для применения алгоритма пузырьковой сортировки. Перейти к редактору
Примечание : Согласно википедии «Сортировка пузырьков, иногда называемая сортировкой по погружению, представляет собой простой алгоритм сортировки, который работает путем многократного перехода по списку для сортировки, сравнения каждой пары соседних элементов и обмена местами. их, если они находятся в неправильном порядке ".
Образец массива : [12, 345, 4, 546, 122, 84, 98, 64, 9, 1, 3223, 455, 23, 234, 213]
Ожидаемый результат : [3223, 546, 455, 345, 234, 213, 122, 98, 84, 64, 23, 12, 9, 4, 1]
Щелкните меня, чтобы увидеть решение25. Напишите функцию JavaScript, которая принимает список названий стран в качестве входных данных и возвращает самое длинное название страны в качестве выходных данных. Перейдите в редактор
S расширенная функция : Longest_Country_Name ([«Австралия», «Германия», «Соединенные Штаты Америки»])
Ожидаемый результат : «Соединенные Штаты Америки»
Щелкните меня, чтобы увидеть раствор26. Напишите функцию JavaScript для поиска самой длинной подстроки в заданной строке без повторяющихся символов.Зайти в редактор
Щелкните меня, чтобы увидеть решение27. Напишите функцию JavaScript, которая возвращает самый длинный палиндром в заданной строке. Заходим в редактор
Примечание. Согласно Википедии: «В информатике самая длинная палиндромная подстрока или самая длинная проблема симметричного фактора -
проблема поиска непрерывной подстроки максимальной длины данной строки, которая также является палиндромом.
Например, самая длинная палиндромная подстрока слова «бананы» - это «анана».Не гарантируется, что самая длинная палиндромная подстрока будет уникальной; Например,
в строке abracadabra нет палиндромной подстроки длиной больше трех,
но есть две палиндромные подстроки длины три, а именно «aca» и «ada».
В некоторых приложениях может потребоваться вернуть все максимальные
палиндромные подстроки (то есть все подстроки, которые сами являются палиндромами и не могут
быть расширенным до более крупных палиндромных подстрок) вместо того, чтобы возвращать только одну подстроку или
возвращает максимальную длину палиндромной подстроки.
Щелкните меня, чтобы увидеть решение28. Напишите программу JavaScript для передачи «функции JavaScript» в качестве параметра. Зайти в редактор
Щелкните меня, чтобы увидеть решение29. Напишите функцию JavaScript, чтобы получить имя функции. Зайти в редактор
Щелкните меня, чтобы увидеть решениеЕще не все!
* Чтобы запустить код, наведите указатель мыши на панель результатов и нажмите кнопку «ПОВТОР». *
См. Pen javascript-common-editor от w3resource (@ w3resource) на CodePen.
Не отправляйте здесь какие-либо решения вышеуказанных упражнений, если вы хотите внести свой вклад, перейдите на соответствующую страницу упражнения.
функций JavaScript
Введение
Все в JavaScript происходит в функциях.
Функция - это самодостаточный блок кода, который можно определить один раз и запускать в любое время.
Функция может дополнительно принимать параметры и возвращать одно значение.
Функции в JavaScript - это объектов , особый вид объектов: функциональных объектов .Их суперсила заключается в том, что их можно вызывать.
Кроме того, функции называются функциями первого класса , потому что они могут быть присвоены значению, могут передаваться как аргументы и использоваться как возвращаемое значение.
Синтаксис
Начнем со «старого» синтаксиса до ES6 / ES2015. Вот объявление функции :
Я использую
foo
иbar
как случайных имен . Введите любое имя, чтобы заменить их.function dosomething (foo) { // сделай что-нибудь }
(теперь, в посте ES6 / ES2015, обозначается как обычная функция )
Функции могут быть присвоены переменным (это называется функциональным выражением ):
const dosomething = function (foo) { // сделай что-нибудь }
Выражения именованных функций похожи, но лучше работают с трассировкой вызовов стека, которая полезна при возникновении ошибки - она содержит имя функции:
const dosomething = function dosomething (foo) { // сделай что-нибудь }
ES6 / ES2015 представил стрелочных функций , которые особенно удобно использовать при работе со встроенными функциями в качестве параметров или обратных вызовов:
const dosomething = foo => { //сделай что-нибудь }
Стрелочные функции имеют важное отличие от других определений функций, приведенных выше, мы увидим, какое из них позже, поскольку это более сложная тема.
Параметры
Функция может иметь один или несколько параметров.
const dosomething = () => { //сделай что-нибудь } const dosomethingElse = foo => { //сделай что-нибудь } const dosomethingElseAgain = (foo, bar) => { //сделай что-нибудь }
Начиная с ES6 / ES2015, функции могут иметь значения по умолчанию для параметров:
const dosomething = (foo = 1, bar = 'hey') => { //сделай что-нибудь }
Это позволяет вызывать функцию без заполнения всех параметров:
что-нибудь (3) сделай что-нибудь()
ES2018 представил конечные запятые для параметров, функцию, которая помогает уменьшить количество ошибок из-за отсутствия запятых при перемещении параметров (например,грамм. перемещая последнюю в середину):
const dosomething = (foo = 1, bar = 'hey') => { //сделай что-нибудь } dosomething (2, 'хо!')
Вы можете обернуть все свои аргументы в массив и использовать оператор распространения Оператор при вызове функции:
const dosomething = (foo = 1, bar = 'hey') => { //сделай что-нибудь } const args = [2, 'хо!'] что-то (... аргументы)
При большом количестве параметров запомнить порядок может быть сложно.Используя объекты, деструктуризация позволяет сохранить имена параметров:
const dosomething = ({foo = 1, bar = 'hey'}) => { //сделай что-нибудь console.log (foo) // 2 console.log (bar) // 'хо!' } const args = {foo: 2, bar: 'ho!' } что-то (аргументы)
Возвращаемые значения
Каждая функция возвращает значение, которое по умолчанию -
undefined
.Любая функция завершается, когда заканчиваются ее строки кода или когда поток выполнения находит ключевое слово
return
.Когда JavaScript встречает это ключевое слово, он завершает выполнение функции и возвращает управление вызывающей стороне.
Если вы передаете значение, это значение возвращается как результат функции:
const dosomething = () => { вернуть "тест" } const result = dosomething () // результат === 'test'
Вы можете вернуть только одно значение.
Чтобы имитировал , возвращающий несколько значений, вы можете вернуть литерал объекта или массив и использовать деструктурирующее присвоение при вызове функции.
Использование массивов:
Использование объектов:
Вложенные функции
Функции могут быть определены внутри других функций:
const dosomething = () => { const dosomethingelse = () => { // здесь какой-то код } dosomethingelse () вернуть "тест" }
Вложенная функция ограничена внешней функцией и не может быть вызвана извне.
Это означает, что
dosomethingelse ()
недоступно извнеdosomething ()
:const dosomething = () => { const dosomethingelse = () => { // здесь какой-то код } dosomethingelse () вернуть "тест" } dosomethingelse () // ReferenceError: dosomethingelse не определено
Это очень полезно, потому что мы можем создать инкапсулированный код, который ограничен в своей области внешней функцией, в которой он определен.
У нас может быть 2 функции, определяющие функцию с тем же именем, внутри них:
const bark = () => { const dosomethingelse = () => { // здесь какой-то код } dosomethingelse () вернуть "тест" } const sleep = () => { const dosomethingelse = () => { // здесь какой-то код } dosomethingelse () вернуть "тест" }
и, самое главное, вам не нужно думать о перезаписи существующих функций и переменных, определенных внутри других функций.
Методы объекта
При использовании в качестве свойств объекта функции называются методами:
const car = { марка: 'Ford', модель: 'Fiesta', start: function () { console.log (`Начато`) } } car.start ()
это
в стрелочных функцияхСуществует важное поведение стрелочных функций по сравнению с обычными функциями при использовании в качестве методов объекта. Рассмотрим этот пример:
const car = { марка: 'Ford', модель: 'Fiesta', start: function () { консоль.log (`Запущен $ {this.brand} $ {this.model}`) }, стоп: () => { console.log (`Остановлен $ {this.brand} $ {this.model}`) } }
Метод
stop ()
не работает должным образом.Это связано с тем, что обработка
и
отличается в двух стилях объявления функций.этот
в стрелочной функции относится к контексту включающей функции, которым в данном случае является объектокна
:
это
, который ссылается на объект хоста с помощью функции()
Это означает, что стрелочных функций не подходят для использования для объектных методов и конструкторов (конструкторы стрелочных функций фактически вызывают
TypeError
при вызове).IIFE - это функция, которая выполняется сразу после объявления:
; (функция dosomething () { console.log ('выполнено') }) ()
Вы можете присвоить результат переменной:
const something = (function dosomething () { вернуть что-то }) ()
Они очень удобны, так как вам не нужно отдельно вызывать функцию после ее определения.
Смотрите мой пост, посвященный им.
Подъем функций
JavaScript перед выполнением вашего кода переупорядочивает его в соответствии с некоторыми правилами.
Функции, в частности, перемещаются в верхнюю часть своей области видимости. Вот почему можно написать
что-нибудь () function dosomething () { console.log ('что-то сделал') }
Внутренне JavaScript перемещает функцию перед ее вызовом вместе со всеми другими функциями, находящимися в той же области:
function dosomething () { console.log ('что-то сделал') } сделай что-нибудь()
Теперь, если вы используете именованные функциональные выражения, поскольку вы используете переменные, произойдет что-то другое.Поднимается объявление переменной, но не значение, поэтому не функция.
что-нибудь () const dosomething = function dosomething () { console.log ('что-то сделал') }
Не выйдет на работу:
Это потому, что происходит внутреннее:
const dosomething сделай что-нибудь() dosomething = function dosomething () { console.log ('что-то сделал') }
То же самое и с объявлениями
let
.Объявленияvar
тоже не работают, но с другой ошибкой:Это связано с тем, что объявления
var
поднимаются и инициализируются значениемundefined
, тогда какconst
иlet
поднимаются, но не инициализируются..