Функции на встроенном языке 1С:Предприятие 8.3: от объявления до вызова в нужном модуле
Содержание
Функция во встроенном языке 1С:Предприятие 8.3 объявляется ключевым словом Функция, закрывается строкой КонецФункции и обязательно возвращает значение через оператор Возврат. Простейший пример — три строки:
Функция СуммаСНДС(Сумма, Ставка = 20) Экспорт
Возврат Сумма + Сумма * Ставка / 100;
КонецФункции
Главное отличие функции от процедуры — именно возвращаемое значение. Процедура выполняет действия и ничего не отдаёт наружу, функцию же вызывают там, где нужен результат: в присваивании, в условии, в выражении. Тип этого результата заранее не объявляют — язык 1С не требует указывать, что вернётся: число, строка, булево или ссылка.
Ниже — синтаксис объявления, как передавать параметры, чем Возврат в функции отличается от Возврат в процедуре, где физически размещать код и какие ошибки повторяются у начинающих разработчиков.
Чем функция отличается от процедуры
Процедура и функция — два вида методов. Пишутся почти одинаково, разница в одном: функция возвращает значение, процедура — нет.
| Признак | Процедура | Функция |
|---|---|---|
| Ключевые слова | Процедура … КонецПроцедуры | Функция … КонецФункции |
| Возврат значения | нет | да, через Возврат Значение; |
Оператор Возврат | без значения, досрочный выход | со значением, обязателен |
| Где вызывают | отдельной строкой | в выражении, присваивании, условии |
Процедуру вызывают командой — отдельной инструкцией:
ПровестиДокумент(Ссылка);
Функцию вызывают там, где ждут результат:
Итого = СуммаСНДС(1000); // 1200
Если СуммаСНДС(1000) > Лимит Тогда
// ...
КонецЕсли;
Практическое правило: если метод отвечает на вопрос «сколько?», «какой?», «истина или ложь?» — это функция. Если он что-то делает («провести», «записать», «заполнить») — процедура.
Формально вызвать функцию можно и как процедуру — отдельной строкой, проигнорировав результат. Платформа это допускает, но смысла в таком вызове обычно нет: вы посчитали значение и выбросили его.
Синтаксис объявления и имя метода
Полная форма объявления выглядит так:
&НаСервереБезКонтекста
Функция РассчитатьМаржу(Выручка, Знач Себестоимость, Валюта = Неопределено) Экспорт
// тело функции
Возврат Результат;
КонецФункции
Разберём по частям:
- 🏷 Директива компиляции (
&НаСервере,&НаКлиентеи др.) — ставится строкой выше объявления. Нужна в модулях управляемых форм и общих модулях; в модулях объектов её не пишут. - 🔑 Ключевое слово
Функция. - 📛 Имя — любое допустимое имя: буквы, цифры, подчёркивание, не начинается с цифры. Русские и латинские имена работают одинаково, но в пределах проекта лучше держать один язык. Имя должно говорить, что метод возвращает:
ПолучитьКурсВалюты, а неОбработка1. ( )Список параметров — может быть пустым.- 🟡 Экспорт — необязательное слово. С ним метод виден из других модулей, без него — только внутри своего.
Имя функции не может совпадать с именем другого метода или переменной в том же модуле. Если объявить две функции с одинаковым именем, компилятор выдаст ошибку «Метод с указанным именем уже определён» ещё до запуска.
Параметры: по значению и по ссылке, значения по умолчанию
Параметры перечисляют в скобках через запятую. В языке 1С у них две важные особенности, на которых спотыкаются чаще всего.
Передача по ссылке — поведение по умолчанию. Если внутри метода изменить значение параметра, изменится и переменная, которую передали при вызове:
Процедура Удвоить(Число)
Число = Число * 2;
КонецПроцедуры
Х = 5;
Удвоить(Х); // теперь Х = 10
Передача по значению — через ключевое слово Знач. Внутри метода работает копия, исходная переменная не меняется:
Процедура Удвоить(Знач Число)
Число = Число * 2; // меняется только копия
КонецПроцедуры
Х = 5;
Удвоить(Х); // Х по-прежнему 5
Передача по ссылке касается и структур, таблиц значений, массивов. Даже если такой параметр объявлен со Знач, копируется только сама ссылка на коллекцию, а не её содержимое — очистив массив внутри метода, вы очистите его и снаружи. Знач защищает от переприсваивания, но не от изменения внутренностей коллекции.
Значения по умолчанию задают через = после имени параметра. Такой параметр при вызове можно не указывать:
Функция Округлить(Число, Разрядность = 2)
Возврат Окр(Число, Разрядность);
КонецФункции
Округлить(3.14159); // 3.14 — взято умолчание
Округлить(3.14159, 0); // 3
Параметры со значением по умолчанию обязаны идти последними — после обязательных. Чтобы пропустить средний необязательный параметр, оставляют пустое место между запятыми: Метод(А, , В).
Оператор Возврат и типы возвращаемого значения
В функции Возврат обязателен и несёт значение. Как только выполнение дошло до Возврат, функция завершается и отдаёт указанное значение в точку вызова.
Функция ЗнакЧисла(Число)
Если Число > 0 Тогда
Возврат 1;
ИначеЕсли Число < 0 Тогда
Возврат -1;
КонецЕсли;
Возврат 0;
КонецФункции
Здесь три точки выхода. Важно, чтобы значение возвращалось по любой ветке условий. Если выполнение дойдёт до КонецФункции, не встретив Возврат, функция вернёт Неопределено — и это частый источник плавающих ошибок: код работает на одних данных и падает на других.
Тип значения жёстко не задан. Одна и та же функция теоретически может вернуть и число, и строку в разных ветках — платформа это разрешит. На практике так делать не стоит: вызывающий код рассчитывает на один тип. Если функция логически возвращает «значение или ничего», возвращайте Неопределено как признак отсутствия результата и проверяйте его на стороне вызова.
Как вернуть несколько значений. Функция отдаёт одно значение, но это значение может быть коллекцией. Когда расчёт выдаёт сразу несколько результатов, возвращают структуру с именованными полями — это читаемее, чем набор параметров по ссылке:
Функция РазложитьСумму(Сумма, Ставка = 20)
Результат = Новый Структура;
Результат.Вставить("НДС", Сумма * Ставка / (100 + Ставка));
Результат.Вставить("БезНДС", Сумма - Сумма * Ставка / (100 + Ставка));
Возврат Результат;
КонецФункции
Вызывающий код получает структуру и обращается к полям по имени: Разбор = РазложитьСумму(1200); НДС = Разбор.НДС;. Для суммы 1200 и ставки 20 это 1200 × 20 / 120 = 200.
В процедуре Возврат тоже допустим, но без значения — он просто досрочно прерывает выполнение:
Процедура Обработать(Элемент)
Если Не ЗначениеЗаполнено(Элемент) Тогда
Возврат; // выходим, дальше не идём
КонецЕсли;
// основная логика
КонецПроцедуры
Где размещать код: модули и область видимости
Функция живёт внутри модуля. От того, в каком модуле она объявлена и стоит ли у неё Экспорт, зависит, откуда её можно вызвать.
Основные типы модулей и что в них держать:
- 🧩 Общие модули (ветка
Общие → Общие модулив дереве конфигурации) — для переиспользуемого кода, который нужен в разных местах. Именно сюда выносят функции-утилиты: расчёты, преобразования, обращения к данным. - 📄 Модуль формы — обработчики событий формы и её элементов, вспомогательные методы конкретной формы.
- 📦 Модуль объекта (документа, справочника) — логика самого объекта: заполнение, проверка, события записи и проведения.
- 🗂 Модуль менеджера — методы, относящиеся не к конкретному элементу, а к объекту в целом.
Область видимости определяется словом Экспорт:
- Без
Экспортфункция видна только внутри своего модуля — это локальный метод. - С
Экспортв общем модуле — функция доступна отовсюду через имя модуля:ОбщегоНазначения.РассчитатьМаржу(...). - С
Экспортв модуле объекта — доступна через ссылку на объект.
У общих модулей есть флажки контекста компиляции, которые задают в палитре свойств модуля: «Сервер», «Клиент (управляемое приложение)», «Вызов сервера». Они определяют, откуда разрешено вызывать экспортные методы модуля. Для функции, которая обращается к базе данных, нужен серверный контекст — иначе получите ошибку компиляции о недоступности метода на клиенте.
Директивы компиляции над самим объявлением уточняют это для конкретного метода. Самые ходовые:
&НаСервере— метод формы, выполняется на сервере, имеет доступ к данным формы.&НаКлиенте— выполняется на клиенте, работает с интерфейсом и реагирует на действия пользователя.&НаСервереБезКонтекста— серверный метод без доступа к данным формы. Самый лёгкий по ресурсам: контекст формы на сервер не передаётся. Для чистых расчётов выбирайте именно его.
Если директиву в модуле управляемой формы не указать, метод компилируется по умолчанию как серверный. В общих модулях директиву над методом не ставят — контекст задают флажками всего модуля.
Если один и тот же расчёт нужен в нескольких документах — не копируйте функцию в каждый модуль объекта. Вынесите её в общий модуль с Экспорт и вызывайте оттуда. Меньше дублирования — меньше мест для правки.
Практический пример: функция расчёта показателя
Соберём всё вместе. Задача — посчитать рентабельность
по выручке и себестоимости, с защитой от деления на ноль. Функцию разместим в общем модуле ОбщегоНазначения с директивой &НаСервереБезКонтекста — она не трогает ни форму, ни контекст объекта, только считает.
&НаСервереБезКонтекста
Функция РентабельностьПродаж(Знач Выручка, Знач Себестоимость) Экспорт
Если Выручка = 0 Тогда
Возврат 0;
КонецЕсли;
Прибыль = Выручка - Себестоимость;
Рентабельность = Прибыль / Выручка * 100;
Возврат Окр(Рентабельность, 2);
КонецФункции
Вызов из модуля формы или другого метода:
Показатель = ОбщегоНазначения.РентабельностьПродаж(1000, 600);
// Прибыль = 400, 400 / 1000 * 100 = 40, результат 40
Разберём, что здесь сделано правильно:
- Защита граничного случая — проверка
Выручка = 0стоит в начале, до деления. Иначе при нулевой выручке функция упадёт с ошибкой деления на ноль. - Параметры со
Знач— функция только читает их, менять исходные переменные ей незачем. - Единственный тип результата — всегда число. Нет веток, которые вернули бы строку или
Неопределено. - Говорящее имя — по вызову понятно, что получим рентабельность в процентах.
Частые ошибки при создании функций
Эти ошибки повторяются у тех, кто переходит на встроенный язык 1С с других языков или только начинает.
- 🔴 Функция не возвращает значение по всем веткам. Условие покрыло не все случаи, и в одной из веток нет
Возврат. Результат —Неопределенотам, где ждали число. Лечится финальнымВозвратпосле всех условий. - 🔴 Вызов серверной функции с клиента напрямую. В управляемом приложении код клиента и сервера разделён директивами. Клиентский метод не может напрямую дёрнуть серверную функцию, работающую с базой. Нужен серверный вызов — через общий модуль с флагом «Вызов сервера» или серверный метод формы.
- 🔴 Изменение параметра без
Значпо невнимательности. Передали переменную в метод, внутри её переприсвоили — и снаружи она неожиданно изменилась. Если менять исходные данные не нужно, ставьтеЗнач. - 🔴 Необязательный параметр не в конце. Параметр со значением по умолчанию стоит перед обязательным — компилятор не пропустит.
- 🔴 Тяжёлые вычисления и запросы внутри цикла по строкам. Функция дёргает базу на каждой итерации. Для таблицы в тысячи строк это десятки секунд. Запрос выносят за цикл, а в функцию передают уже готовые данные.
Про производительность стоит добавить отдельно. Функция сама по себе не медленная — медленным её делает содержимое. Два приёма, которые реально ускоряют код:
- Не обращайтесь к базе данных внутри функции, вызываемой в цикле. Получите все нужные данные одним запросом заранее.
- Избегайте неконтролируемой рекурсии. Функция, вызывающая саму себя без условия выхода, уйдёт в бесконечный цикл и упадёт по стеку вызовов. Условие завершения рекурсии проверяйте в первых строках.
Частые вопросы
Можно ли вызвать функцию из другого модуля?
Экспорт и модуль это допускает. Функцию из общего модуля вызывают через имя модуля: ИмяМодуля.ИмяФункции(...). Без Экспорт метод доступен только внутри своего модуля.Обязательно ли указывать тип возвращаемого значения?
Чем `Возврат` в функции отличается от `Возврат` в процедуре?
Возврат несёт значение и обязателен — Возврат Итог;. В процедуре он пишется без значения и просто досрочно завершает выполнение.