Функции
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
, а не на свойстве
объекта функции.
Объект 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
Непосредственно перед входом в тело функции, автоматически создается объект 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)
Функции — Kotlin
В Kotlin функции объявляются с помощью ключевого слова fun
.
fun double(x: Int): Int { return 2 * x }
Использование функций
При вызове функции используется традиционный подход:
val result = double(2)
Для вызова вложенной функции используется знак точки.
Stream().read() //создаёт экземпляр класса Stream и вызывает read()
Параметры
Параметры функции записываются аналогично системе обозначений в языке Pascal — имя: тип. Параметры разделены запятыми. Каждый параметр должен быть явно указан.
fun powerOf(number: Int, exponent: Int): Int { /*...*/ }
Вы можете использовать завершающую запятую при объявлении параметров функции.
fun powerOf( number: Int, exponent: Int, // завершающая запятая ) { /*...*/ }
Аргументы по умолчанию
Параметры функции могут иметь значения по умолчанию, которые используются в случае, если аргумент функции не указан при её вызове. Это позволяет снизить уровень перегруженности кода.
fun read( b: ByteArray, off: Int = 0, len: Int = b.size, ) { /*...*/ }
Значения по умолчанию указываются после типа знаком =
.
Переопределённые методы всегда используют те же самые значения по умолчанию, что и их базовые методы. При переопределении методов со значениями по умолчанию в сигнатуре эти параметры должны быть опущены.
open class A { open fun foo(i: Int = 10) { /*...*/ } } class B : A() { override fun foo(i: Int) { /*...*/ } // значение по умолчанию указать нельзя }
Если параметр по умолчанию предшествует параметру без значения по умолчанию, значение по умолчанию можно использовать только при вызове функции с именованными аргументами.
fun foo( bar: Int = 0, baz: Int, ) { /*...*/ } foo(baz = 1) // Используется значение по умолчанию bar = 0
Но если последний аргумент после параметров по умолчанию — лямбда, вы можете передать её либо как именованный аргумент, либо за скобками.
fun foo( bar: Int = 0, baz: Int = 1, qux: () -> Unit, ) { /*...*/ } foo(1) { println("hello") } // Используется значение по умолчанию baz = 1 foo(qux = { println("hello") }) // Используется оба значения по умолчанию: bar = 0 и baz = 1 foo { println("hello") } // Используется оба значения по умолчанию: bar = 0 и baz = 1
Именованные аргументы
При вызове функции вы можете явно указать имена одного или нескольких аргументов. Это может быть полезно, когда у функции
большой список аргументов, и сложно связать значение с аргументом, особенно если это логическое или null
значение.
При явном указывании имен аргументов в вызове функции, вы можете свободно изменять порядок их перечисления, и, если вы хотите использовать их значения по умолчанию, вы можете просто пропустить эти аргументы.
Рассмотрим следующую функцию reformat()
, которая имеет 4 аргумента со значениями по умолчанию:
fun reformat( str: String, normalizeCase: Boolean = true, upperCaseFirstLetter: Boolean = true, divideByCamelHumps: Boolean = false, wordSeparator: Char = ' ', ) { /*...*/ }
При её вызове, вам не нужно явно указывать все имена аргументов.
reformat( "String!", false, upperCaseFirstLetter = false, divideByCamelHumps = true, '_' )
Вы можете пропустить все аргументы со значением по умолчанию.
reformat("This is a long String!")
Вы также можете пропустить не только все аргументы со значениями по умолчанию, но и лишь некоторые из них. Однако после первого пропущенного аргумента вы должны указывать имена всех последующих аргументов.
reformat("This is a short String!", upperCaseFirstLetter = false, wordSeparator = '_')
Вы можете передать переменное количество аргументов (vararg
) с именами,
используя оператор spread
.
fun foo(vararg strings: String) { /*...*/ } foo(strings = *arrayOf("a", "b", "c"))
В JVM: синтаксис именованных аргументов не может быть использован при вызове Java функций, потому как байт-код Java не всегда сохраняет имена параметров функции.
Функции с возвращаемым типом Unit
Если функция не возвращает никакого полезного значения, её возвращаемый тип — Unit
. Unit
— тип только с одним
значением — Unit
. Это значение не нуждается в явном указании возвращения функции.
fun printHello(name: String?): Unit { if (name != null) println("Hello $name") else println("Hi there!") // `return Unit` или `return` необязательны }
Указание типа Unit
в качестве возвращаемого значения тоже не является обязательным. Код, написанный выше, и следующий
код совершенно идентичны:
fun printHello(name: String?) { /*...*/ }
Функции с одним выражением
Когда функция возвращает одно единственное выражение, фигурные скобки { }
могут быть опущены, и тело функции может
быть описано после знака =
.
fun double(x: Int): Int = x * 2
Явное объявление возвращаемого типа является необязательным, когда он может быть определен компилятором.
fun double(x: Int) = x * 2
Явные типы возвращаемых значений
Функции с блочным телом всегда должны иметь явно указанный возвращаемый ими тип данных, если только они не предназначены
для возврата Unit
, тогда указание типа возвращаемого значения необязательно.
Kotlin самостоятельно не вычисляет тип возвращаемого значения для функций с блочным телом, потому что подобные функции могут иметь сложную структуру, и возвращаемый тип будет неочевидным для читающего этот код человека (иногда даже для компилятора).
Нефиксированное число аргументов (varargs)
Параметр функции (обычно для этого используется последний) может быть помечен модификатором vararg
.
fun <T> asList(vararg ts: T): List<T> { val result = ArrayList<T>() for (t in ts) // ts - это массив (Array) result. add(t) return result }
Это позволит указать несколько значений в качестве аргументов функции.
val list = asList(1, 2, 3)
Внутри функции параметр с меткой vararg
и типом T
виден как массив элементов T
, таким образом переменная ts
в
вышеуказанном примере имеет тип Array<out T>
.
Только один параметр может быть помечен как vararg
. Если параметр с именем vararg
не стоит на последнем месте в
списке аргументов, значения для последующих параметров могут быть переданы только с использованием синтаксиса именованных
аргументов. В случае, если параметр является функцией, для этих целей можно вынести лямбду за фигурные скобки.
При вызове vararg
-функции вы можете передать аргументы один за другим, например asList(1, 2, 3)
, или, если у нас уже
есть необходимый массив элементов и вы хотите передать его содержимое в функцию, используйте оператор spread
(необходимо пометить массив знаком *
).
val a = arrayOf(1, 2, 3) val list = asList(-1, 0, *a, 4)
Если вы хотите передать массив примитивного типа в vararg
, вам необходимо
преобразовать его в обычный (типизированный) массив с помощью функции toTypedArray()
.
val a = intArrayOf(1, 2, 3) // IntArray - массив примитивного типа val list = asList(-1, 0, *a.toTypedArray(), 4)
Инфиксная запись
Функции, помеченные ключевым словом infix
, могут вызываться с использованием инфиксной записи (без точки и
скобок для вызова). Инфиксные функции должны соответствовать следующим требованиям:
- Они должны являться членом другой функции или расширения;
- В них должен использоваться только один параметр;
- Параметр не должен принимать переменное количество аргументов и не должен иметь значения по умолчанию.
infix fun Int.shl(x: Int): Int { /*...*/ } // вызов функции, с использованием инфиксной записи 1 shl 2 // то же самое, что 1. shl(2)
Вызовы инфиксных функций имеют более низкий приоритет, чем арифметические операторы, приведение типов и оператор
rangeTo
. Следующие выражения эквивалентны:
1 shl 2 + 3
эквивалентно1 shl (2 + 3)
,0 until n * 2
эквивалентно0 until (n * 2)
,xs union ys as Set<*>
эквивалентноxs union (ys as Set<*>)
.С другой стороны, приоритет вызова инфиксной функции выше, чем у логических операторов
&&
и||
,is
— иin
-проверок и некоторых других операторов. Эти выражения также эквивалентны:
a && b xor c
эквивалентноa && (b xor c)
,a xor b in c
эквивалентно(a xor b) in c
.
Обратите внимание, что инфиксные функции всегда требуют указания как получателя, так и параметра. Когда вы вызываете
метод на текущем приемнике, используя инфиксную запись, явно используйте this
. Это необходимо для обеспечения
однозначного синтаксического анализа.
class MyStringCollection { infix fun add(s: String) { /*...*/ } fun build() { this add "abc" // Верно add("abc") // Верно //add "abc" // Не верно: получатель должен быть указан } }
Область видимости функций
В Kotlin функции могут быть объявлены в самом начале файла, что значит, что вам необязательно создавать класс, чтобы воспользоваться его функцией (как в Java, C# или Scala). В дополнение к этому, функции в Kotlin могут быть объявлены локально, как функции-члены и функции-расширения.
Локальные функции
Kotlin поддерживает локальные функции, т.е. функции, вложенные в другие функции.
fun dfs(graph: Graph) { fun dfs(current: Vertex, visited: MutableSet<Vertex>) { if (!visited.add(current)) return for (v in current. neighbors) dfs(v, visited) } dfs(graph.vertices[0], HashSet()) }
Локальная функция может иметь доступ к локальным переменным внешних по отношению к ним функций (типа closure). Таким
образом, в примере, приведённом выше, visited
может быть локальной переменной.
fun dfs(graph: Graph) { val visited = HashSet<Vertex>() fun dfs(current: Vertex) { if (!visited.add(current)) return for (v in current.neighbors) dfs(v) } dfs(graph.vertices[0]) }
Функции-члены
Функции-члены — это функции, объявленные внутри классов или объектов.
class Sample { fun foo() { print("Foo") } }
Функции-члены вызываются с использованием точки.
Sample().foo() // создаёт инстанс класса Sample и вызывает его функцию foo
Для более подробной информации о классах и их элементах см. Классы и Наследование.
Функции-обобщения
Функции могут иметь обобщённые параметры, которые задаются треугольными скобками и помещаются перед именем функции. -15 private fun findFixPoint(): Double { var x = 1.0 while (true) { val y = Math.cos(x) if (Math.abs(x — y) < eps) return x x = Math.cos(x) } }
Для соответствия требованиям модификатора tailrec
, функция должна вызывать сама себя в качестве последней операции,
которую она предпринимает. Вы не можете использовать хвостовую рекурсию, когда существует ещё какой-то код после вызова
этой самой рекурсии. Также нельзя использовать её внутри блоков try
/catch
/finally
или в open
функциях. На данный
момент хвостовая рекурсия поддерживается только в backend виртуальной машины Java (JVM) и в Kotlin/Native.
См. также:
- Встроенные функции,
- Функции-расширения,
- Высокоуровневые функции и лямбды.
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"}} <дел>Функция А
Еще кое-что здесь...