Ленивый, компонуемый и модульный JavaScript
Хорошей практикой считается делать код компонуемым и модульным. Он упрощается и к его частям становится проще обращаться несколько раз. Для JavaScript-разработчиков тоже появились инструменты для использования этого подхода.
Остановимся на использовании четырех возможностей ECMAScript: итераторах, генераторах, «жирных» стрелочных функциях и операторе for-of
в сочетании с функциями высшего порядка, композициями функций, отложенными вычислениями.
Прежде чем погрузиться с головой в пример, рассмотрим некоторые общие понятия.
Функции высшего порядка
Функция высшего порядка — это функция, которая удовлетворяет хотя бы одному из условий:
- принимает в качестве аргументов одну или более функций;
- возвращает функцию как результат.
Вы наверняка сталкивались с функциями высшего порядка, если писали обработчик событий или применяли Array.prototype.map()
.
Например, функция, попадая в Array. prototype.map
, ничего не знает о её структуре и методах. Единственное знание — механизм обработки своих входящих данных, поэтому может быть неоднократно применима как для отдельных значений, так и коллекций.
Композиции функций
Композиция функций — применение одной функции к результату другой. В результате чего из простых функций составляются сложные.
Например, есть две функции: f
и g
. Результат композиции (f, g)
— функция f(g(x))
, которую так же можно использовать в композиции или передать как параметр функции высшего порядка.
Допустим, поставили задачу написать программу, которая на входе принимает файл и возвращает массив, содержащий количество гласных в каждом слове на каждой строке. Одно из решений — написать одну большую функцию, которая делает это.
В монолитном решении, представленном ниже, используется новый оператор for-of
вместо привычного for-loop
для перебора значений массива. Итератор — контейнер, который реализует протокол последовательного перебора и возвращает с помощью оператора yield
значения по одному (массивы, строки, генераторы и т. д.).
function vowelCount(file) { let contents = readFile(file) let lines = contents.split('\n') // преобразуем содержимое в массив строк. let result = [] // массив массивов, где каждый индекс соответствует строке // и каждый индекс в пределах массива — кол-во гласных. for (let line of lines) { let temp = [] let words = line.split(/\s+/) for (let word of words) { let vowelCount = 0 for (let char of word) { if ( 'a' === char || 'e' === char || 'i' === char || 'o' === char || 'u' === char ) vowelCount++ } temp.push(vowelCount) } result.push(temp) } return result }
Это решение не расширяемое, не масштабируемое и не содержит повторно используемых компонентов. Альтернативный подход состоит в использовании функций высшего порядка и композиции.
// main.js function vowelOccurrences(file) { return map(words => map(vowelCount, words), listOfWordsByLine(read(file))) } function vowelCount(word) { return reduce((prev, char) => { if ( 'a' === char || 'e' === char || 'i' === char || 'o' === char || 'u' === char ) return ++prev else return prev }, 0, word) } function listOfWordsByLine(string) { return map(line => split(/\s+/, line), split('\n', string)) } // повторно используемые функции из библиотеки util.js function reduce(fn, accumulator, list) { return [].reduce.call(list, fn, accumulator) } function map(fn, list) { return [].map.call(list, fn) } function split(splitOn, string) { return string.split(splitOn) }
listOfWordsByLine
возвращает массив массивов, где каждый элемент соответствует массиву слов, составляющих строку.
let input = 'line one\nline two' listOfWordsByLine(input) // [['line','one'],['line','two']]
В примере vowelCount
подсчитывает количество гласных в слове, vowelOccurrences
использует vowelCount
на выходе listOfWordsByLine
для расчета гласных в каждой строке.
Профит второго способа — универсальные функции, которые будут полезны в дальнейшей работе и будут комбинироваться вместе для решения больших задач.
Таким образом, они приводят к подходу «снизу вверх», в результате чего код становится компонуемым и модульным.
Отложенные вычисления
Рассмотрим на примере «ленивую» обработку данных и построение «ленивых» цепочек вычислений (pipelines).
Дан список целых чисел. Необходимо возвести в квадрат элементы списка и вывести сумму первых четырех полученных значений.
Для написания «ленивой» реализации выясним, когда понадобится произвести вычисления. При суммировании первых четырёх квадратов нужно возвести в квадрат эти элементы, поэтому операцию возведения в квадрат отложим до начала суммирования.
let squareAndSum = (iterator, n) => { let result = 0 while(n > 0) { try { result += Math.pow(iterator.next(), 2) n-- } catch(_) { // длина перечня меньше `n` следовательно // iterator.next сообщает, что у него нет значений break } } return result } let getIterator = (arr) => { let i = 0 return { next: function() { if (i < arr.length) return arr[i++] else throw new Error('Iteration done') } } } let squareAndSumFirst4 = (arr) => { let iterator = getIterator(arr) return squareAndSum(iterator, 4) }
Возводим в степень элементы только тогда, когда начинается суммирование.
yield
обрабатываются только те элементы, которые будут участвовать в итоге. Итерация реализуется таким образом, что элементы возвращаются с помощью оператора yield
по одному вплоть до получения сигнала об отсутствии элементов для вывода. Протокол инкапсулируется в объект итератора, который содержит одну функцию — next
, принимающую нулевые значения. Следующий элемент возвращается, только при наличии элементов.Функция squareAndSum
принимает в качестве входных данных итератор и
(число элементов в сумме). С помощью вызова метода .next()
n
раз получает из итератора n
значений, возводит каждый из элементов в квадрат и суммирует их.
GetIterator
возвращает итератор, сформированный из нашего списка.
squareAndSumFirst4
использует getIterator
и squareAndSum
, чтобы вернуть сумму первых четырех чисел из входного списка, возведённых в квадрат «ленивым» способом. Использование итераторов позволяет внедрять структуры данных, которые могут вернуть с помощью оператора
бесконечные значения.
Необходимость выполнения описанных выше действий каждый раз, когда нам нужен итератор, усложняет написание кода. К счастью, ES, начиная с версии 6, предлагает простой способ описания итераторов — генераторы.
Генератор — функция, работу которой можно приостановить, а потом возобновить. Причём генератор может выдать значения несколько раз в ходе исполнения с помощью ключевого слова yield
. При вызове возвращает объект-генератор. С помощью метода .next
получается следующее значение. В JavaScript генераторы создаются путём определения функции с *
.
// генератор, который возвращает бесконечный список последовательных // чисел, начиная с 0 // знак "*" тспользуется, чтобы сообщить обработчику, что это генератор function* numbers() { let i = 0 yield 'бесконечный список чисел' while (true) yield i++ } let n = numbers() // получить итератор от генератора n.next() // {value: "бесконечный список чисел", done: false} n.next() // {value: 0, done: false} n.next() // {value: 1, done: false} // и так далее..
Генераторы поддерживают оба протокола, поэтому получить значения можно с помощью оператора for-of
.
for (let n of numbers()) console.log(n) // печатать бесконечный список чисел
Теперь реализуем задачу, которая покажет, как эти три подхода помогают очистить код приложения.
Пример: задача и решение
Исходные данные:
- файл, который на каждой строке содержит имя пользователя и по размеру превышает объём оперативной памяти используемого устройства;
- функция, которая считывает блок данных с диска и возвращает его же, дополнив символом новой строки.
Задача:
- Получить имена пользователей, которые начинаются с «A», «E» или «M».
- Выполнить запрос с использованием полученных данных к странице
http://jsonplaceholder.
. - Применить к первым четырём ответам сервера заданный набор из четырёх функций.
Содержимое файла:
Bret Antonette Samantha Karianne Kamren Leopoldo_Corkery Elwyn.Skiles Maxime_Nienow Delphine Moriah.Stanton
Разбиваем задачу на блоки поменьше, чтобы для каждого написать отдельную функцию. В результате получаем следующие функции:
- Первая возвращает каждое имя (
getNextUsername
). - Вторая отбирает имена, которые начинаются с «A», «E»или «M» (
filterIfStartsWithAEOrM
). - Третья делает сетевой запрос и возвращает
Promise
, объект-заглушку для вывода результата вычисления (makeRequest
).
Эти функции оперируют значениями. Для того, чтобы применить их к списку, введём три функции высшего порядка:
- Первая выбирает элементы списка на основе заданных параметров (
filter
). - Вторая применяет функцию к каждому элементу списка (
map
). - Третья применяет функции из одного итератора к данным другого итератора (zipWith c функцией упаковки).
«Ленивость» этого подхода может принести пользу, так как сетевые запросы делаются не для всех подходящих под критерии фильтрации имен.
Итак, у нас есть массив функций, которые должны выполняться для обработки окончательных ответов, и функция, которая возвращает «ленивым» способом блоки данных. Напишем функцию с применением генераторов для получения имен пользователей, используя «ленивый» подход.
// функции, которые выполняются в ответ на запрос let fnsToRunOnResponse = [f1, f2, f3, f4] // возвращает следующий блок данных из файла // символ * обозначает, что эта функция является генератором в JavaScript function* getNextChunk() { yield 'Bret\nAntonette\nSamantha\nKarianne\nKamren\nLeopoldo_Corkery\nElwyn.Skiles\nMaxime_Nienow\nDelphine\nMoriah. Stanton\n' } // getNextUsername принимает итератор, который возвращает следующий фрагмент, заканчивающийся переводом строки // он сам возвращает итератор, который возвращает имена по одному function* getNextUsername(getNextChunk) { for (let chunk of getNextChunk()) { let lines = chunk.split('\n') for (let l of lines) if (l !== '') yield l } }
Теперь для работы со значениями необходимы следующие функции:
- Первая возвращает
True
, если значение удовлетворяет критериям фильтра,False
— в противном случае. - Вторя возвращает URL-адрес при получении имени пользователя.
- Третья при получении URL-адреса делает запрос и возвращает
Promise
для этого запроса.
Promise
— «контейнер» для хранения значения выполняемой операции, которое появится в будущем. Интерфейс Promise
позволяет определить, какие действия выполнять после успешного завершения операции или сбоя. Если операция пройдёт успешно, вызывается обработчик удачного результата со значением операции. В противном случае вызывается обработчик ошибки.
// эта функция возвращает True, если имя пользователя соответствует нашим критериям // и false в противном случае let filterIfStartsWithAEOrM = username => { let firstChar = username[0] return 'A' === firstChar || 'E' === firstChar || 'M' === firstChar } // makeRequest делает AJAX-запрос к URL и возвращает promise // он использует новый API и fat arrows es6 // это обычная функция, не генератор let makeRequest = url => fetch(url).then(response => response.json()) // makeUrl принимает имя пользователя и генерирует URL-адрес, к которому хотим обратиться let makeUrl = username => 'http://jsonplaceholder.typicode.com/users?username=' + username
Теперь напишем функции высшего порядка, которые обеспечат работу с «ленивыми» списками. Их задача — отложить выполнение до тех пор, пока не появится запрос. В этом случае нужны значения по требованию, поэтому на помощь придут генераторы.
// функция filter принимает другую функцию (предикат). Предикат же принимает значение и возвращает // булевое значение и сам итератор. Filter возвращает итератор, если предикат при обработке // входного значения возвращает True. function* filter(p, a) { for (let x of a) if (p(x)) yield x } // map принимает на входе функцию и итератор // возвращает новый итератор, который возвращает результат применения функции к каждому значению // входного итератора function* map(f, a) { for (let x of a) yield f(x) } // zipWith принимает булеву функцию и два итератора в качестве входных данных // возвращает итератор, который в свою очередь применяет заданную функцию к значениям из каждого // итератора и выдаёт результат function* zipWith(f, a, b) { let aIterator = a[Symbol.iterator]() let bIterator = b[Symbol. iterator]() let aObj, bObj while (true) { aObj = aIterator.next() if (aObj.done) break bObj = bIterator.next() if (bObj.done) break yield f(aObj.value, bObj.value) } } // execute запускает отложенный итератор // как правильно неоднократно обращается к `.next` итератора // вплоть до выполнения итератора function execute(iterator) { for (x of iterator) ;; // извлекаем значения итератора }
Необходимые функции написаны. Используем композицию, чтобы решить поставленную задачу.
let filteredUsernames = filter(filterIfStartsWithAEOrM, getNextUsername(getNextChunk) let urls = map(makeUrl, filteredUsernames) let requestPromises = map(makeRequest, urls) let applyFnToPromiseResponse = (fn, promise) => promise.then(response => fn(response)) let lazyComposedResult = zipWith(applyFnToPromiseResponse, fnsToRunOnResponse, requestPromises) execute(lazyComposedResult)
lazyComposedResult
— «ленивая» цепочка вычислений (pipeline), составленная из композиций функций. Ни одно звено не выполнится, пока не запущен верхний блок композиции, то есть lazyComposedResult
. Мы сделали только четыре вызова, хотя результат фильтрации может содержать более четырех значений.
В итоге получили лаконичное решение задачи, в котором появились функции высшего уровня, композиции и повторно используемые функции.
Перевод статьи «Lazy, composable, and modular JavaScript»
Обзор функций в JavaScript ES6.
В течение последних нескольких лет JavaScript изменялся. И вот 12 новых фичей, которые вы можете начать использовать уже сегодня!
1. История JavaScript
Новые дополнения к языку называются ECMAScript 6. Они также упоминаются как ES6 или ES2015+. Начиная с концепции 1995 года, JavaScript развивался медленно. Новые дополнения выходили раз в несколько лет. ECMAScript появился в 1997 году, чтобы указать путь JavaScript. Были выпущены такие его версии: ES3, ES5, ES6 и другие.
Как вы заметили, между ES3, ES5 и ES6 существуют промежутки в 10 и 6 лет. Новая стратегия состоит в том, чтобы делать небольшие постепенные изменения каждый год. Вместо того, чтобы делать большие изменения сразу, как это случилось с ES6.
2. Поддержка браузеров
Все современные браузеры и среды программирования уже поддерживают ES6!
Chrome, MS Edge, Firefox, Safari, Node и многие другие уже имеют встроенную поддержку большинства функций JavaScript ES6. Таким образом, всё, что вы собираетесь изучить в этом туториале, вы можете начать использовать прямо сейчас. Давайте начнем с ECMAScript 6!
3. Основные функции ES6
Вы можете проверить все эти фрагменты кода на консоли своего браузера.
Так что не верьте мне на слово и тестируйте каждый пример ES5 и ES6.
3.1. Блочная область видимости переменных
В ES6 мы перешли от объявления переменных с var на использование let/const.
Что не так с var? Проблема var – это утечка переменной в другой блок кода, например, в циклы for или if-блоки.
Для test(false) вы ожидаете возвращения outer, но нет, вы получаете undefined. Почему?
Потому что даже при том, что if-блок не выполняется, выражение var x в строке 4 «поднимается».
Поднятие переменных:
- var является переменной области видимости. Она доступна во всей функции даже до того, как её объявят.
- Выражения «поднимаются». Так что вы сможете использовать переменные до их объявления.
- Инициализация НЕ поднимется. Если вы используете var, ВСЕГДА объявляйте ваши переменные наверху.
- После применения правил подъема, мы можем лучше понять, что же случилось.
- ECMAScript 2015 идёт на помощь:
Изменение var на let приводит к тому, что всё работает так, как и ожидалось. Если блок if не вызывается, переменная x не поднимается из блока.
Взглянём на поднятие и «временные мёртвые зоны»:
- В ES6 let будет поднимать переменную наверх блока (НЕ наверх функции, как это происходит в ES5).
- Однако ссылка на переменную в блоке перед объявлением этой переменной приводит к ReferenceError.
- let – переменная области видимости. Вы не можете использовать её, пока она не будет объявлена.
- «Временные мёртвые зоны» – это зоны в начале блока и до того места, где объявляется переменная.
IIFE (Immediately-Invoked Function Expression)
Перед объяснением IIFE взгляните на пример:
Как вы видите, появляется private. Для того, чтобы удержать его, вам необходимо использовать IIFE (немедленно-вызываемое выражение функции):
Если вы посмотрите на jQuery/lodash или другие open source проекты, то заметите, что они используют IIFE во избежание загрязнения глобальной среды и определения только глобального, например _,$ или jQuery.
ES6 гораздо проще, нам больше не нужно использовать IIFE, когда мы просто можем применить блоки и let:
Const
Вы также можете использовать const, если не хотите, чтобы переменная изменялась вообще.
3.2. Литералы шаблонов
Нам больше не нужно встраивать конкатенации, когда у нас есть литералы шаблонов. Взгляните:
Сейчас вы можете использовать кавычку (`) и строковую интерполяцию ${}:
3.3. Многострочные строки
Нам больше не нужно конкатенации строк + \n по типу:
В ES6 мы снова можем использовать кавычку для решения такого примера:
Оба фрагмента кода будут иметь точно такой же результат.
3.4. Назначение деструктуризации
Деструктуризация в ES6 очень полезная и точная.
Получение элементов с массива
То же самое:
Обмен значений
Так же:
Деструктуризация для нескольких возвращаемых значений
В строке 3 вы также можете вернуть ее в массив подобный тому, что на примере (и сохранить некоторые, набрав код):
Но потом необходимо подумать о порядке возврата данных.
В ES6 вызывающий выбирает только те данные, которые ему нужны (строка 6):
Обратите внимание: В строке 3 есть некоторые другие функции ES6. Мы можем сжать { left: left} только до { left}.Посмотрите, насколько это компактнее по сравнению с версией ES5. Разве не круто?
Деструктуризация для параметров согласования
Так же, но короче:
Deep Matching
Так же, но короче:
Это также называют деструктуризацией объектов.
Как видите, деструктуризация весьма полезна и способствует хорошему стилю программирования.
Практический опыт:
- Используйте деструктуризацию массива для получения элементов или замены переменных. Это избавит вас от создания временных ссылок.
- Не используйте деструктуризацию массива для нескольких возвращаемых значений, вместо этого примените деструктуризацию объекта.
3.
5. Классы и ОбъектыС ECMAScript 6 мы перешли от «функции-конструктора» к «классам».
В JavaScript каждый отдельный объект имеет прототип, который является другим объектом. Все объекты JavaScript наследуют свои методы и свойства от своего прототипа.
В ES5 мы использовали объектно-ориентированное программирование (ООП), применяя функцию- конструктор для создания объектов, следующим образом:
Видео курсы по схожей тематике:
Создание адаптивного SPA с Angular
Сергей Патёха
Платформа Managed Extensibility Framework (MEF)
Давид Бояров
Photoshop. Базовый курс для web-разработчика
Сергей Воропаев
В ES6 имеется некий синтаксический сахар. Мы можем делать то же самое менее шаблонно и с новыми ключевыми словами, такими как class и constructor. Также обратите внимание на то, как мы определяем методы constructor.prototype. speak = function () vs speak():
Как видим, оба стиля (ES5/6) дают одинаковые результаты и используются одинаково.
Практический опыт:
- Всегда используйте синтаксис класса и избегайте прямого манипулирования прототипом. Почему? Потому что это делает код более кратким и понятным.
- Избегайте наличия пустого конструктора. Классы имеют конструктор по умолчанию, если он не указан.
3.6. Наследование
Опираемся на предыдущий класс Animal. Предположим, мы хотим расширить его и определить класс Lion.
В ES5 это большей частью связано с прототипическим наследованием.
Я не буду описывать все детали, но заметьте:
- В строке 3 мы явно вызываем конструктор Animal с параметрами.
- В строках 7-8 мы назначили прототип Lion прототипу Animal.
- В строке 11 мы вызываем метод speak из родительского класса Animal.
В ES6 у нас есть новые ключевые слова extends и super.
Посмотрите, насколько разборчиво выглядит этот код ES6 по сравнению с ES5, и они работают одинаково!
Практический опыт:
- Используйте встроенный способ наследования с extends.
3.7. Native Promises
Мы перешли от callback hell к promises.
У нас есть одна функция, которая при done получает обратный вызов для выполнения. Мы должны выполнить этот вызов дважды один за другим. Вот почему во второй раз мы вызываем printAfterTimeout.
Правда, это может пойти наперекосяк, если вам нужен третий или четвёртый обратный вызов. Давайте посмотрим, как мы можем сделать это с промисами:
Как видите, мы с промисами мы можем использовать then, чтобы сделать что-либо после выполнения другой функции. Больше не нужно хранить вложенные функции.
3.8. Стрелочные функции
ES6 не удалил выражения функций, но добавил новые функции – стрелочные.
В ES5 были некоторые проблемы с this:
Вам нужно использовать временное this для ссылки внутри функции или использовать bind. В ES6 вы можете использовать стрелочную функцию.
3.9. For…of
Мы перешли от for к forEach, и потом к for…of:
В ES6 for…of также позволяет нам делать итерации.
3.10. Параметры по умолчанию
Мы перешли от проверки того, была ли переменная определена, к присвоению значения параметрам по умолчанию (default parameters). Вы делали что-то подобное раньше?
Вероятно, это обычный шаблон для проверки того, имеет ли переменная значение или присваивает значение по умолчанию. Однако заметьте, что здесь есть некоторые спорные вопросы:
- В строке 8 мы задали 0, 0 и получили 0, -1
- В строке 9 мы задали false, но получили true.
Если в качестве параметра по умолчанию задано значение boolean или это значение равно нулю, то оно не будет работать. Знаете почему? Все расскажем после примера ES6. С ES6 вы можете писать код лучше и короче!
Обратите внимание на строки 5 и 6 – мы получаем ожидаемые результаты. Пример ES5 не работает. Сначала мы должны проверить undefined, поскольку false, null, undefined и 0 являются фальшивыми значениями. Мы можем выбраться с такими цифрами:
Сейчас всё работает так, как и должно, когда мы проверяем undefined.
3.11. Rest-параметры
Мы перешли от аргументов к rest-параметрам и spread-оператору. Получать произвольное количество аргументов на ES5 довольно неудобно:
Мы можем сделать то же, используя rest-оператор . . . .
3.12. Spread-оператор
Бесплатные вебинары по схожей тематике:
Создание дизайна лендинга с Figma.
Алла Штогрина
Что надо знать для веб разработки. (реальная разработка + обзор вакансий)»
Михаил Храбан
Firebase. Организация удаленной работы с данными.
Тысячный Влад
Мы пришли от apply() до spread-оператора. Опять на помощь приходит . . .:
Напоминание: мы используем apply () для преобразования массива в список аргументов. Например, Math.max () принимает список параметров, но, если у нас есть массив, мы можем использовать apply, чтобы заставить его работать.
Как мы видели ранее, мы можем использовать apply для передачи массивов в виде списка аргументов:
В ES6 вы можете использовать spread-оператор:
Кроме того, мы пришли от использования массивов contact к использованию spread-оператора:
В ES6 вы можете сглаживать вложенные массивы, используя оператор spread:
4. Заключение
JavaScript прошёл через множество изменений. В этой статье описываются основные функции, которые должен знать каждый разработчик JavaScript. Кроме того, в статье есть примеры из практического опыта, которые помогут сделать ваш код более кратким и простым для понимания.
Материал подготовлен на основе статьи из блога Adrian Mejia
Функции Js — Learn.
coЦели
- Написать функцию, которая возвращает значение
- Напишите функцию, которая принимает параметр
- Напишите функцию, которая принимает несколько параметров
Введение
Вы будете писать свое решение на index.js
.
В этой лабораторной работе мы будем развивать наши навыки общения с помощью JavaScript. У нас праздничное настроение, поэтому мы собираемся подвести итоги общих праздничных поздравлений. как функции, чтобы нам не приходилось повторяться. Красота функций заключается в том, что мы могли бы повторно использовать эти функции для текста поздравительных открыток, для устные приветствия, тексты песен и т. д…
Шаблонные литералы
Существует два основных способа включения переменных в строку. Скажем, у нас есть переменная с именем date
, которой мы присваиваем значение:
var date = "3 июля"
В JavaScript мы можем использовать операторы для конкатенации (объединения) двух строк или, в данном случае, строки и переменной, например:
console. log("Мой день рождения " + дата)
При заданной дате
приведенный выше код будет регистрировать Мой день рождения 3 июля
. Однако,
используя слегка измененный синтаксис, мы можем добиться того же, вставив
переменную в строку. Они называются шаблонными
литералы
и переписав приведенный выше console.log
с одним, будет выглядеть так:
console.log(`Мой день рождения ${date}`)
Это также запишет Мой день рождения 3 июля
.
Теперь есть два важных изменения в синтаксисе: Любые переменные, которые мы хотим
include должен быть заключен в фигурные скобки ( { }
) и перед ним стоит знак доллара.
($
). Кроме того, вместо одиночных '
или двойных кавычек '
мы должен использовать обратные кавычки, `
. Если обратные кавычки не используются, JavaScript будет
интерпретировать знак доллара и фигурные скобки как обычную часть строки ,
в результате получается Мой день рождения ${date}
! Любое выражение может быть включено в
литералы шаблона, а не только переменные, поэтому мы могли бы написать что-то вроде:
console. log(`У меня есть ${1 + 1} питомцы`)
И получить У меня есть 2 питомца
. Обратите внимание, что это не будет регистрировать то же самое, если вы сделали
следующее:
console.log("У меня есть " + 1 + 1 + " домашние животные")
Это логи У меня 11 домашних животных
! JavaScript преобразует оба 1
в строки, а не сначала складывает их вместе. Вам нужно будет написать следующее, чтобы получить тот же результат, что и литералы шаблона:
console.log("У меня есть " + (1 + 1) + " домашние животные")
Вы можете использовать операторы или литералы шаблонов для построения больших строк с динамическими значениями. Литералы шаблонов — это просто способ сделать ваш код немного легче читать и помочь гарантировать, что JavaScript не интерпретируется неправильно, когда объединение различных типов данных в строки, как мы только что видели.
Инструкции
Напишите функцию с именем
happyHolidays
. Эта функция не должна принимать параметры и должны вернуть строку"С праздником!"
.Напишите функцию с именем
happyHolidaysTo
. Эта функция должна принимать параметр имени человека, которого вы хотите поздравить с праздником, и вернуть строку`Счастливых праздников, ${name}!`
Напишите функцию с именем
happyCustomHolidayTo
. Эта функция должна принимать два параметры, праздник, в который хотите пожелать удачи, и название человек, которому вы желаете добра. Порядок параметров имеет значение, поэтому обязательно сначала укажите праздник, а затем имя. Эта функция должна возвращать строку`С праздником ${праздник}, ${имя}!`
Напишите функцию с именем
HolidayCountdown
. Эта функция должна принимать два параметры, название праздника и количество дней до него. функция должна возвращать строку`До ${дней} дней до ${праздника}!`
. Обратите внимание, что хотядней
используется первым при построении возвращаемой строки,holidayCountdown
должен сначала введите название праздника, а затем количество дней до него.
АВЕДЕВ | | 4,5 | |
СРЕДНЯЯ | | 7,5 | |
СРЕДНЯЯ | | 7,5 | |
СРЕДНИЙЕСЛИ | | 3,5 | |
СРЕДНИЕ | | 6 | |
БЕТАДИСТ | | 0,6854705810117458 | |
БЕТАИНВ | | 1.9999999999999998 | |
БИНОМРАСП | | 0,205078125 | |
КОРРЕЛ | | 0,9970544855015815 | |
СЧЕТ | | 4 | |
КОНТА | | 4 | |
СЧИТАТЬ ПУСТО | | 2 | |
СЧЁТЕСЛИ | | 3 | |
СЧЕТИСЧИСЛ | | 2 | |
СТРАНА УНИКАЛЬНАЯ | | 3 | |
КОВАРИАЦИЯP | | 5. 2 | |
КОВАРИАНТЫ | | 9,666666666666668 | |
DEVSQ | | 115 | |
ЭКСПОНДИСТ | | 0,8646647167633873 | |
FРАСП | | 0,0012237917087831735 | |
ФИНВ | | 0,10930991412457851 | |
ФИШЕР | | 0,9729550745276566 | |
ФИШЕРИНВ | | 0,75 | |
ПРОГНОЗ | | 10. 607253086419755 | |
ЧАСТОТА | | 1,2,4,2 | |
ГАММА | | 1.32934039143 | |
ГАММАЛН | | 12.801827480081961 | |
ГАУСС | | 0,4772498680518208 | |
ГЕОМЕАН | | 5. 656854249492381 | |
РОСТ | | 32.00000000000003 | |
ХАРМЕЙН | | 4.266666666666667 | |
ГИПЕОМРАСП | | 0,363261093 | 87 |
ПЕРЕХВАТ | | 0,04838709677419217 | |
КУРТ | | -0,15179963720841627 | |
БОЛЬШОЙ | | 5 | |
ЛИНЕЙН | | 2,1 | |
ЛОГНОРМРАСП | | 0,03 | 557068005|
ЛОГНОРМИНВ | | 557068005, 3,5, 1,2, правда) 4.000000000000001 | |
МАКС | | 0,8 | |
МАКСА | | 1 | |
МЕДИАНА | | 3,5 | |
МИН | | 0,1 | |
МИНА | | 0 | |
МОДЕМУЛЬТ | | 2,3 | |
МОДЕСНГЛ | | 2 | |
НОРМРАСП | | 0, | 87802741321 |
НОРМИНВ | | ||
НОРМРАСП | | 0,8413447460685429 | |
НОРМСТОБР | | 1. 0000000000000002 | |
ПИРСОН | | 0,6993786061802354 | |
ПРОЦЕНТИЛЬEXC | | 1,5 | |
ПРОЦЕНТИЛЬ | | 1,9 | |
PERCENTRANKEXC | | 0,4 | |
ПРОЦЕНТРАНГ | | 0,33 | |
ПЕРМУТ | | 970200 | |
ПЕРЕСТАНОВКА | | 64 | |
PHI | | 0,30113743215480443 | |
ПУАССОНА | | 0,12465201948308113 | |
ПРОБ | | 0,4 | |
КВАРТИЛЬEXC | | 1,25 | |
КВАРТИЛЕЙНК | | 1,75 | |
СРЕДНИЙ РАНГ | | 4,5 | |
РАНКЕК | | 4 | |
RSQ | | 0,4891304347826088 | |
НАКЛОН | | 0,3595430714067974 | |
НАКЛОН | | 0,303193339354144 | |
НАКЛОН | | 2 | |
МАЛЕНЬКИЙ | | 3 | |
СТАНДАРТНЫЙ | | 1.3333333333333333 | |
СТДЕВА | | 6.013872850889572 | |
СТДЕВП | | 5.361 | |
СТДЕВПА | | 5. 489889697333535 | |
СТАНДОТКЛОН | | 6.191391873668904 | |
СТЕЙКС | | 3.305718950210041 | |
СТЬЮДРАСП | | 0,9946953263673741 | |
ИНН | | 59.99999999996535 | |
ТРИММЕАН | | 3.7777777777777777 | |
ВАРА | | 36.16666666666667 | |
ВАРП | | 28,75 | |
ВАРПА | | 30. |