Функции
Update: Более новый материал по этой теме находится по адресу https://learn.javascript.ru/function-basics.
- Создание функций
- Функции — объекты
- Области видимости
- Параметры функции
- Работа с неопределенным числом параметров
- Пример передачи функции по ссылке
- Сворачивание параметров в объект
В этой статье описаны функции Javascript на уровне языка: создание, параметры, приемы работы, замыкания и многое другое.
Существует 3 способа создать функцию. Основное отличие в результате их работы — в том, что именованная функция видна везде, а анонимная — только после объявления:
| Именованные | Анонимные (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)
Куда вставлять Javasсript
Чтобы код Javascript сработал в браузере пользователя, его необходимо вставить между тегами <script> и </script>.
<html>
<head>
<title>Пример JavaScript</title>
</head>
<body>
<script>
document.getElementById("demo").innerHTML = "Первый сценарий на JavaScript";
</script>
<noscript>
Ваш браузер не поддерживает JavaScript или поддержка отключена в настройках.
</noscript>
<div></div>
</body>
</html>
В данном примере, как только HTML страница будет загружена, браузер запустит команду document.
getElementById(«demo»).innerHTML = «Первый сценарий на JavaScript», которая ищет элемент с идентификатором «demo» и, найдя его, помещает в него строку «Первый сценарий на JavaScript».
В действительности полная запись тега <script> имеет следующий вид: <script type=»text/javascript»>. В атрибуте type указывается используемый язык скриптов. Однако в настоящее время существует не так уж много таких языков, и в HTML язык Javascript установлен как язык скриптов по умолчанию. Поэтому атрибут type использовать не нужно.
Также, обратите внимание на теги
Функции и события JavaScript
Функция JavaScript — это блок кода, который выполняется по «вызову».
Например, функция может вызываться при возникновении какого-нибудь события, вроде нажатия пользователем на кнопку мыши.
Подробнее о функциях и событиях вы узнаете позже в соответствующих главах.
JavaScript в теге <head> и <body>
В HTML документ можно вставлять любое число скриптов.
На HTML странице скрипты можно размещать внутри секции <body> или <head>, либо в обоих сразу.
В следующем примере функция JavaScript размещена в секции <head>. Эта функция вызывается при нажатии пользователем на кнопку:
<!DOCTYPE html>
<html>
<head>
<script>
function myFunction() {
document.getElementById("demo").innerHTML = "Параграф изменен.";
}
</script>
</head>
<body>
<h2>Веб-страница</h2>
<p>Параграф</p>
<button type="button">Изменить</button>
</body>
</html>
В следующем примере функция JavaScript размещена в секции <body>:
<!DOCTYPE html>
<html>
<body>
<h2>Веб-страница</h2>
<p>Параграф</p>
<button type="button">Изменить</button>
<script>
function myFunction() {
document.
getElementById("demo").innerHTML = "Параграф изменен.";
}
</script>
</body>
</html>
Размещение скриптов в нижней части элемента <body> увеличивает скорость отображения HTML документа, так как компиляция скриптов замедляет рендеринг веб-страницы.
Внешний JavaScript
Скрипты также можно размещать во внешних файлах:
Внешний файл: myScript.js
function myFunction() {
document.getElementById("demo").innerHTML = "Параграф изменен.";
}
Внешние скрипты оправданы тогда, когда нужно один и тот же сценарий использовать в нескольких HTML документах.
Обычно у файлов JavaScript расширение .js.
Чтобы использовать внешний скрипт, нужно поместить имя файла Javascript в атрибут src тега <script>:
<script src="//msiter.ru/myScript.js"></script>
Подключать внешние скрипты можно внутри тега <head> или <body>.
Скрипт будет вести себя так же, как если бы он был вставлен непосредственно внутри тега <script>.
Внимание! Внешние скрипты не могут содержать теги <script>.
Преимущества внешних JavaScript
У размещения скриптов во внешних файлах есть ряд преимуществ:
- Разделяется HTML и Javascript код
- Становится проще читать и обслуживать HTML и JavaScript
- Благодаря кешированию файлов JavaScript увеличивается скорость загрузки веб-страницы
Чтобы добавить несколько файлов скриптов на HTML страницу, достаточно вставить нужное число тегов <script>:
<script src="//msiter.ru/myScript1.js"></script> <script src="//msiter.ru/myScript2.js"></script>
Внешние ссылки
В качестве ссылок на внешние скрипты может использоваться либо абсолютный URL, либо относительный путь к текущей веб-странице.
В следующем примере для подключения внешнего скрипта используется полный URL:
<script src="//msiter.ru/js/myScript1.js"></script>
В следующем примере скрипт расположен в подкаталоге текущего веб-сайта:
<script src="//msiter.ru/js/myScript1.js"></script>
В следующем примере скрипт расположен в том же подкаталоге что и текущая страница:
<script src="//msiter.ru/myScript1.js"></script>
Устаревшие и нестандартные браузеры
Для обеспечения совместимости со старыми браузерами, которые не умеют работать со скриптами Javascript, код скрипта размещают внутри тегов комментариев HTML <!— и —>. Если пренебречь этой предосторожностью, то браузер, не знающий Javascript, просто выведет на экран код скрипта в виде обычного текста. К счастью таких нестандартных браузеров в настоящее время практически не осталось. Однако подобный код все еще встречается, и стоит знать для чего он используется.
<html>
<head>
<title>Пример JavaScript</title>
</head>
<body>
<script type="text/javascript">
<!--
document.write("Привет, Мир!")
// -->
</script>
</body>
</html>
В приведенном выше примере сразу же после тега <script type=»text/javascript»> вставлен открывающий тег HTML комментария <!—, а перед тегом </script> вставлен закрывающий тег комментария —>.
При этом перед закрывающим тегом HTML комментария поставлен двойной прямой слэш //, который является уже комментарием JavaScript, показывающий, что строка после него является комментарием с точки зрения JavaScript. Назначения этого комментария JavaScript — указать браузерам, умеющим работат с JavaScript, что символы → следует проигнорировать.
secondToFormattedString(seconds) | Строка возврата «чч:мм:сс» |
exec(f, аргументы, контекст) | Функция или код Exec с аргументами и контекстом |
isOutsider(el) | Проверить элемент за пределы поля зрения |
в окне просмотра (эл) | Проверить элемент в окне просмотра |
длина объекта(el) | Возвращает длину объекта.![]() |
процентов(а, б, г) | Возвращает процентное значение для b из a. Если r истинно, возвращаемое значение является целым числом, иначе — реальным | .
objectShift(obj) | Вернуть смещенный объект. Удалить элемент с первым ключом |
objectDelete(объект, ключ) | Удалить ключ из объекта и вернуть его |
объектКлон(объект) | Скопировать объект для клонирования и вернуть его |
arrayDelete(arr, val) | Удалить из массива по значению и вернуть его |
arrayDeleteByKey(арр, ключ) | Удалить ключ из массива и вернуть его |
arrayDeleteByMultipleKeys(arr, keys_array) | Удалить ключ из массива и вернуть новый массив |
nvl(данные, прочее) | Проверить данные на нулевое значение и вернуть другое, если нулевое |
github (репо, обратный вызов) | Получить информацию с github и добавить ее в обратный вызов |
обнаружитьIE() | Определить, является ли браузер Internet Explorer или Edge |
обнаружитьChrome() | Определить, является ли браузер Chrome |
md5(str) | Кодировать строку с помощью алгоритма md5 |
encodeUri(str) | Исправление базовой функции encodeUri |
pageHeight() | Вернуть реальную высоту страницы |
cleanPreCode(селектор) | Удалить пробелы из кода |
координат(эл) | Возвращает координаты элемента как объект {сверху, слева} |
positionXY(событие, тип) | Позиция возврата для типа: экран, страница, клиент |
клиентXY(событие) | Возврат позиции |
страницаXY(событие) | Возврат позиции |
экранXY(событие) | Возврат позиции |
isRightMouse(событие) | Проверить, нажимает ли пользователь правую кнопку мыши |
hiddenElementSize(el, includeMargin) | Возвращает размер элемента как объект {ширина, высота} |
getStyle(эль, псевдо) | Возвращает рассчитанные стили элемента |
getStyleOne(el, property) | Возвращает рассчитанные стили элемента для указанного свойства |
getTransformMatrix(el) | Матрица преобразования возвращаемого элемента |
вычисленныйRgbToHex(rgb) | Возвращает шестнадцатеричное значение для вычисляемого цвета элемента.![]() |
вычисленныйRgbToHex(rgb) | Возвращает шестнадцатеричное значение для строки rgb ‘rgb(x, x, x)’ => #xxxxxx |
вычисленныйRgbToRgba(rgb,альфа) | Возвращает строку rgba для строки rgb ‘rgb(x, x, x)’ => ‘rgba(x, x, x, a)’ |
вычисленныйRgbToArray(rgb) | Возвращаемый массив для строки ‘rgb(x, x, x)’ => [x, x, x] |
hexColorToArray(c) | Преобразование шестнадцатеричного значения цвета в массив |
hexColorToRgbA(c, a) | Преобразовать шестнадцатеричное значение цвета в строку rgba: ‘#xxxxxx’ => ‘rgba(x, x, x, a)’ |
getInlineStyles(el) | Возврат встроенных стилей элемента в виде массива |
updateURIParameter(uri, key, val) | Параметр обновления в Uri |
getURIParameter(uri, ключ) | получить параметр от Uri |
getLocales() | Получить зарегистрированные метрополитены |
addLocale(данные) | Зарегистрировать локаль метро во время выполнения |
aspectRatioH(ширина, соотношение) | Высота возврата для определенного соотношения |
соотношение сторон(высота, соотношение) | Ширина возврата для определенного соотношения |
значениеВОбъекте(объект, значение) | Проверить, существует ли значение в объекте |
keyInObject(объект, ключ) | Проверить, существует ли ключ в объекте |
новыйCssSheet(носитель) | Создать объект листа css |
addCssRule(лист, селектор, правила, индекс) | Добавить правило в объект листа css |
медиа(запрос) | Проверить медиа-запрос |
медиарежимы() | Получить текущие медиа-баллы |
mediaExist(m) | Возвращает true, если точка существует в текущем носителе |
в Медиа(м) | Проверить, является ли точка текущим носителем |
isValue(val) | Вернуть true, если значение не в [undefined, null, «»] |
Отрицательное (значение) | Возвращает истину, если значение меньше 0 |
isPositive(val) | Вернуть true, если значение больше 0 |
isZero(val) | Возвращает true, если val равно 0 (целое число или число с плавающей запятой) |
между(значение, низ, верх, равно) | Возвращает true, если значение находится между нижним и верхним |
parseMoney(val) | Возвращает числовое значение из любого денежного формата. Пример: 5 640,63 долл. США -> 5 640,63 | .
функция(строка) | Функциональный объект возврата |
ближайший (значение, точность, уменьшение) | поиск ближайшего целого числа, кратного искомому. Пример: Metro.utils.nearest(37, 5, false) -> 40, Metro.utils.nearest(37, 5, true) -> 35 |
копия(эл) | Копировать элемент в буфер обмена |
isLocalhost() | Проверить, является ли текущее местоположение локальным хостом |
getCursorPosition(элемент, событие) | Возвращает позицию мыши или указателя как x, y |
getCursorPositionX(элемент, событие) | Возврат позиции мыши или указателя x |
getCursorPositionY(элемент, событие) | Вернуть позицию мыши или указателя y |
Использование функций внутри шаблонов Go
В этом руководстве мы расскажем, как использовать функции шаблонов, такие как и , eq и index , чтобы добавить некоторую базовую логику в наши шаблоны.
Как только у нас будет достаточно хорошее понимание того, как использовать эти функции, мы рассмотрим, как добавить некоторые пользовательские функции в наши шаблоны и использовать их.
Эта статья является частью серии
Это третья часть из четырех статей, посвященных html/template (и text/template ) пакетов в Go. Если вы еще этого не сделали, я предлагаю вам ознакомиться с остальной частью серии здесь: Введение в шаблоны в Go. Их не обязательно читать, но я думаю, они вам понравятся.
Если вам нравится эта серия, подпишитесь на мою рассылку, чтобы получать уведомления, когда я выпускаю новые подобные статьи. Обещаю не спамить.
Функция
и По умолчанию , если 9Действие 0006 в шаблонах будет оценивать, является ли аргумент пустым, но что происходит, когда вы хотите оценить несколько аргументов? Вы можете написать вложенные блоки if/else , но это быстро станет некрасивым.
Вместо этого пакет html/template предоставляет функции и .
Его использование похоже на то, как вы использовали бы функции и в Лиспе (другом языке программирования). Это легче показать, чем объяснить, поэтому давайте просто перейдем к коду. открыть main.go и добавьте следующее:
пакет main
импорт (
"html/шаблон"
"сеть/http"
)
var testTemplate *template.Template
тип Пользовательская структура {
Бул администратора
}
введите структуру ViewData {
*Пользователь
}
основная функция () {
вар ошибка ошибка
testTemplate, ошибка = template.ParseFiles("hello.gohtml")
если ошибка != ноль {
паника (ошибка)
}
http.HandleFunc("/", обработчик)
http.ListenAndServe(":3000", ноль)
}
func handler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Тип контента", "текст/html")
vd := ViewData{&User{true}}
ошибка: = testTemplate.Execute(w, vd)
если ошибка != ноль {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
Затем откройте hello.gohtml и добавьте в свой шаблон следующее.
{{if и .User .User.Admin}}
Вы пользователь с правами администратора!
{{еще}}
В доступе отказано!
{{конец}}
Если вы запустите этот код, вы должны увидеть вывод Вы являетесь пользователем с правами администратора! . Если вы обновите main.go , чтобы либо не включать объект *User , либо установить для Admin значение false, или даже если вы предоставите nil методу testTemplate.Execute() , вместо этого вы увидите Доступ запрещен! .
Функция и принимает два аргумента, позволяет называть их a и b , а затем выполняет логику, примерно эквивалентную if a then b else a . Самое странное, что и — это действительно функция, а не то, что вы помещаете между двумя переменными. Просто помните, что это функция, а не логическая операция, и все должно быть в порядке.
Аналогично, пакет шаблонов также содержит или , которая работает так же, как и , за исключением того, что при истинном значении происходит короткое замыкание.
Т.е. логика для или a b примерно эквивалентна , если a, то a else b , поэтому b никогда не будет оцениваться, если a не пусто.
Функции сравнения (равно, меньше и т. д.)
До сих пор мы имели дело с относительно простой логикой, вращающейся вокруг того, является ли что-то пустым или нет, но что происходит, когда нам нужно выполнить некоторые сравнения? Например, что, если мы хотим настроить класс объекта в зависимости от того, приближается ли пользователь к превышению лимита использования?
Пакет html/template предоставляет нам несколько классов для сравнения. Это
-
eq— возвращает логическую истинуarg1 == arg2 -
ne- Возвращает логическую истинуarg1 != arg2 -
lt— возвращает логическую истинуarg1 < arg2 -
le— возвращает логическую истинуarg1 <= arg2 -
гт— возвращает логическое значениеarg1 > arg2 -
ge— возвращает логическую истинуarg1 >= arg2
Они используются аналогично тому, как используются и и или , где вы сначала вводите функцию, а затем вводите аргументы.
Например, вы можете использовать следующий код в своем шаблоне, чтобы определить, какой текст отображать в отношении использования их API.
{{if (ge .Usage .Limit)}}
Вы достигли предела использования API. Пожалуйста, обновите или обратитесь в службу поддержки для получения дополнительной помощи.
{{else if (gt .Usage .Warning)}}
Вы использовали {{.Usage}} из {{.Limit}} вызовов API и приближаетесь к своему лимиту. Вы рассматривали возможность обновления?
{{else if (eq .Usage 0)}}
Вы еще не использовали API! Чего ты ждешь?
{{еще}}
Вы использовали {{.Usage}} из {{.Limit}} вызовов API.
{{конец}}
if...else if...else
Если вы следили за этой серией, также стоит отметить, что этот код также демонстрирует, как создать блок if...elseif...else , которые мы еще не рассмотрели. Они работают почти так же, как блок if...else , но они позволяют вам иметь несколько разных условных предложений.
Использование переменных функций
До сих пор мы в основном имели дело со структурами данных внутри наших шаблонов, но что произойдет, если мы захотим вызывать наши собственные функции из шаблона? Например, давайте представим, что у нас есть Тип пользователя , и нам нужно выяснить, есть ли у текущего пользователя разрешение на доступ к нашей корпоративной функции при создании пользовательского интерфейса. Мы могли бы создать структуру клиента для представления и добавить поле для разрешения.
тип ViewData struct {
Карта разрешений[string]bool
}
// или же
введите структуру ViewData {
Структура разрешений {
FeatureA bool
логическое значение FeatureB
}
}
Проблема с этим подходом заключается в том, что нам всегда нужно знать каждую функцию, которая используется в текущем представлении, или если мы вместо этого использовали map[string]bool нам нужно заполнить его значением для каждой возможной функции.
Было бы намного проще, если бы мы могли просто вызывать функцию, когда хотели узнать, есть ли у пользователя доступ к какой-либо функции. Есть несколько способов сделать это в Go, поэтому я собираюсь рассказать о нескольких возможных способах сделать это.
1. Создайте метод для типа
User Первый самый простой — скажем, у нас есть тип User , который мы уже предоставили представлению, мы можем просто добавить HasPermission() для объекта, а затем используйте его. Чтобы увидеть это в действии, добавьте следующее к hello.gohtml .
{{if .User.HasPermission "feature-a"}}
<дел>
Функция А
Еще кое-что здесь...

..
getElementById("demo").innerHTML = "Параграф изменен.";
}
</script>
</body>
</html>
ru/js/myScript1.js"></script>


Пример: 5 640,63 долл. США -> 5 640,63
особенность {
граница: 1px сплошная #eee;
отступ: 10 пикселей;
поле: 5px;
ширина: 45%;
отображение: встроенный блок;
}
.инвалид {
цвет: #ccc;
}
стиль>
io"},
}
ошибка: = testTemplate.Execute(w, vd)
если ошибка != ноль {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
Откройте
Error(w, err.Error(), http.StatusInternalServerError)
}
}
Помните о них при использовании шаблонов!
io",
}
vd := ViewData{пользователь}
ошибка: = testTemplate.Execute(w, vd)
если ошибка != ноль {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
gohtml 

ResponseWriter, r *http.Request) {
w.Header().Set("Тип контента", "текст/html")
пользователь := пользователь{
ID: 1,
Электронная почта: "
Это возможно по двум причинам:
Это действительно мощно, потому что теперь мы можем использовать функцию
В частности, нам нужно создать функцию, которая предоставляет нам объект template.HTML
Error(w, err.Error(), http.StatusInternalServerError)
}
}
Для этого нам нужно определить функцию, которая принимает строку и преобразует ее в шаблон 

