Функции во встроенном языке 1С 8.3: параметры, Экспорт, возврат значения и частые ошибки
Содержание
Функцию в 1С объявляют так: Функция ИмяФункции(Параметры), затем тело, внутри обязательно Возврат Значение;, и в конце КонецФункции. Если функцию нужно вызывать из другого модуля, после скобок добавляют слово Экспорт. Правила синтаксиса одинаковы для всех конфигураций 1С:Предприятия 8.3 — это язык платформы, а не конкретной программы.
Главное отличие функции от процедуры — функция возвращает значение и её можно ставить в выражение: Сумма = РассчитатьСкидку(Цена, 5);. Ниже разобраны параметры, область видимости, директивы компиляции, типичные ошибки компилятора и рабочие примеры.
Синтаксис объявления функции в 1С
Полная форма объявления выглядит так:
// Описание: что делает функция.
//
// Параметры:
// Параметр1 - Число - что означает.
// Параметр2 - Строка - что означает.
//
// Возвращаемое значение:
// Число - что возвращается.
//
Функция ИмяФункции(Параметр1, Знач Параметр2 = "") Экспорт
Перем ЛокальнаяПеременная;
// операторы
Возврат Результат;
КонецФункции
Из чего состоит объявление:
- 🔹
Функция— ключевое слово. В английском варианте синтаксиса —Function. - 🔹 Имя — буквы, цифры и подчёркивание, первым символом не может быть цифра. Ключевые слова языка (
Если,Цикл,Возврат) именем быть не могут. - 🔹 Список параметров в круглых скобках. Скобки пишут, даже если параметров нет:
Функция ТекущийПользовательИмя(). - 🔹
Экспорт— необязательно, открывает функцию для других модулей. - 🔹 Тело — любые операторы языка.
- 🔹
КонецФункции— без точки с запятой. В английском варианте —EndFunction.
Где можно объявлять функции
Функции пишут в любом модуле, где допустимы процедуры: модуль формы, модуль объекта, модуль менеджера, общий модуль, модуль команды, модуль приложения и модуль сеанса. Создать модуль или открыть существующий можно в Конфигураторе или в 1С:EDT: правой кнопкой по объекту в дереве конфигурации, затем открыть нужный модуль.
Внутри модуля порядок такой: сначала объявления переменных модуля (Перем), затем процедуры и функции, в конце — основная программа модуля (если она есть). Писать функцию раньше места вызова не обязательно: компилятор видит все методы модуля целиком, предварительное объявление не нужно.
Локальные переменные
Локальные переменные внутри функции можно объявить через Перем, но такие объявления должны стоять в самом начале тела, до первого исполняемого оператора. Чаще переменную просто создают присваиванием: Итог = 0;. Она живёт только до выхода из функции и снаружи не видна.
Комментарий-описание над функцией (блок «Параметры» и «Возвращаемое значение») не просто украшение. Конфигуратор и EDT подхватывают его в контекстную подсказку при вызове, а коллеге не придётся читать тело, чтобы понять, что передавать.
Параметры функции: по ссылке, Знач и значения по умолчанию
В 1С параметры по умолчанию передаются по ссылке. Если функция изменит параметр, изменится и переменная в вызывающем коде. Ключевое слово Знач перед параметром включает передачу по значению: функция получает копию.
Функция Удвоить(Число)
Число = Число * 2; // изменит переменную вызывающего кода
Возврат Число;
КонецФункции
Функция УдвоитьБезопасно(Знач Число)
Число = Число * 2; // меняется только копия
Возврат Число;
КонецФункции
А = 10;
Б = Удвоить(А); // А = 20, Б = 20
В = 10;
Г = УдвоитьБезопасно(В); // В = 10, Г = 20
Знач копирует только саму переменную. Если в параметре лежит массив, структура или таблица значений, копируется ссылка на коллекцию. Функция с Знач Массив всё равно может добавить или удалить элементы, и вызывающий код это увидит. Чтобы защитить коллекцию, передавайте её копию: для таблицы значений — Таблица.Скопировать(), для массива — новый массив с перебором.
Необязательные параметры
Параметр становится необязательным, если задать ему значение по умолчанию через =:
Функция ЦенаСНДС(Цена, СтавкаПроцентов = 20, Округлять = Истина)
Результат = Цена * (100 + СтавкаПроцентов) / 100;
Если Округлять Тогда
Результат = Окр(Результат, 2);
КонецЕсли;
Возврат Результат;
КонецФункции
Сумма1 = ЦенаСНДС(1000); // 1200
Сумма2 = ЦенаСНДС(1000, 10); // 1100
Сумма3 = ЦенаСНДС(1000, , Ложь); // 1200, ставка взята по умолчанию
Правила для значений по умолчанию:
- Значение — только литерал: число, строка, дата вида
'20260101',Истина,Ложь,Неопределено,NULL. - Вызов функции, выражение или константу конфигурации поставить нельзя. Если нужна, например, текущая дата, задайте по умолчанию
Неопределенои проверьте внутри:Если Дата = Неопределено Тогда Дата = ТекущаяДатаСеанса(); КонецЕсли;. - Пропущенный параметр в середине списка обозначают пустым местом между запятыми, как в примере с
Сумма3. - Удобно ставить необязательные параметры в конец списка: вызовы получаются короче и читаются проще.
Как вернуть несколько значений
Функция возвращает одно значение, но им может быть коллекция. Два рабочих способа:
| Способ | Когда подходит | Минус |
|---|---|---|
Вернуть Структура с полями | Результатов 2–5, нужны понятные имена | Опечатка в имени поля всплывёт только при выполнении |
| Изменить параметры, переданные по ссылке | Нужно «дописать» данные в уже существующую коллекцию | Побочный эффект неочевиден читателю кода |
Вернуть ТаблицаЗначений или Массив | Результат — список строк | Требует описания колонок в комментарии |
Функция ПроверитьИНН(Знач ИНН)
Результат = Новый Структура("Корректен, ТекстОшибки", Истина, "");
ИНН = СокрЛП(ИНН);
Если СтрДлина(ИНН) <> 10 И СтрДлина(ИНН) <> 12 Тогда
Результат.Корректен = Ложь;
Результат.ТекстОшибки = "ИНН должен содержать 10 или 12 цифр";
КонецЕсли;
Возврат Результат;
КонецФункции
Экспорт: как вызвать функцию из другого модуля
Без слова Экспорт функция видна только внутри своего модуля. С ним — снаружи, но способ вызова зависит от того, где функция лежит.
| Где объявлена функция | Как вызвать из другого места |
|---|---|
Общий модуль РаботаСЦенами | РаботаСЦенами.ПолучитьЦену(Товар) |
Модуль менеджера справочника Номенклатура | Справочники.Номенклатура.ПолучитьЦену(Товар) |
| Модуль объекта документа | ДокументОбъект.ПолучитьЦену(Товар) — через полученный объект |
| Модуль формы | Только из этой же формы; снаружи — через полученную форму (Форма.ИмяФункции()) и только в клиентском контексте |
Для общих модулей важны их свойства в палитре свойств: флажки Клиент (управляемое приложение), Сервер, Вызов сервера, Глобальный. Функция общего модуля с флажком только Сервер не вызывается напрямую с клиента. Чтобы клиент мог её вызвать, у модуля должен стоять флажок Вызов сервера.
Если в модуле включён флажок Глобальный, экспортные функции вызываются без имени модуля: ПолучитьЦену(Товар). Пользуйтесь этим осторожно: глобальный контекст растёт, и имена начинают конфликтовать.
Не делайте экспортными все функции подряд. Экспорт — это обещание другим разработчикам, что сигнатура не поменяется. Служебные функции оставляйте внутри модуля, а наружу выносите небольшой программный интерфейс.
Директивы компиляции в модуле формы
В модулях форм перед объявлением ставят директиву — где будет выполняться код:
- 🖥
&НаКлиенте— на клиенте, есть доступ к форме и её элементам. - 🗄
&НаСервере— на сервере, данные формы передаются туда и обратно. - ⚡
&НаСервереБезКонтекста— на сервере без передачи данных формы. Быстрее, но реквизиты формы внутри недоступны: всё нужное передавайте параметрами. - 🔁
&НаКлиентеНаСервереБезКонтекста— функцию можно вызывать и с клиента, и с сервера.
&НаСервереБезКонтекста
Функция ОстатокТовара(Номенклатура)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ЕСТЬNULL(СУММА(Остатки.КоличествоОстаток), 0) КАК Количество
|ИЗ
| РегистрНакопления.ТоварыНаСкладах.Остатки(, Номенклатура = &Номенклатура) КАК Остатки";
Запрос.УстановитьПараметр("Номенклатура", Номенклатура);
Выборка = Запрос.Выполнить().Выбрать();
Выборка.Следующий();
Возврат Выборка.Количество;
КонецФункции
Имя регистра в примере условное — подставьте регистр остатков вашей конфигурации.
Функция или процедура: что выбрать
| Функция | Процедура | |
|---|---|---|
| Возвращает значение | Да, через Возврат Значение; | Нет, Возврат; только прерывает выполнение |
| Можно ставить в выражение | Да: Х = Ф() + 1 | Нет, ошибка компиляции |
| Можно вызвать отдельным оператором | Да, результат просто отбрасывается | Да |
| Окончание | КонецФункции | КонецПроцедуры |
| Обработчики событий формы и объекта | Обычно не используются | Да, обработчики — процедуры |
Практическое правило: если код что-то вычисляет или проверяет и вызывающему нужен ответ — функция. Если код меняет состояние (записывает объект, заполняет реквизиты, выводит сообщение) и ответ не нужен — процедура.
Функция без единого Возврат компилируется, но вернёт Неопределено. Это частая причина «пустого» результата: условие внутри не сработало, до Возврат выполнение не дошло.
Смешанный стиль «процедура меняет параметр по ссылке вместо возврата» встречается в старом коде. Новую логику лучше писать функцией с явным результатом: её проще тестировать и читать.
Ошибки компилятора при объявлении функций
Синтаксическую проверку запускают в Конфигураторе через Текст → Синтаксический контроль или клавишей Ctrl+F7 в открытом модуле. Сообщения, которые чаще всего связаны с объявлением функций:
Ещё три ошибки, которые компилятор не поймает:
- ⚠️ Изменение параметра по ссылке «случайно». Функция переиспользует параметр как рабочую переменную, и у вызывающего кода портятся данные. Лечится
Значили отдельной локальной переменной. - ⚠️ Вызов в неверном контексте. Функция с
&НаСервереБезКонтекстаобращается кОбъектилиЭлементыформы. В режиме выполнения получите ошибку о недоступности переменной. - ⚠️ Разный тип результата в разных ветках. В одной ветке
Возврат 0;, в другойВозврат "";. Вызывающий код ждёт число и падает на сложении. Решите заранее, какой тип возвращается, и опишите его в комментарии.
Практические примеры функций
Склонение слова по числу
Функция СловоПоЧислу(Знач Число, Форма1, Форма2, Форма5)
Число = Цел(Число) % 100;
Если Число >= 11 И Число <= 14 Тогда
Возврат Форма5;
КонецЕсли;
Остаток = Число % 10;
Если Остаток = 1 Тогда
Возврат Форма1;
ИначеЕсли Остаток >= 2 И Остаток <= 4 Тогда
Возврат Форма2;
КонецЕсли;
Возврат Форма5;
КонецФункции
// СловоПоЧислу(21, "день", "дня", "дней") -> "день"
// СловоПоЧислу(12, "день", "дня", "дней") -> "дней"
// СловоПоЧислу(103, "день", "дня", "дней") -> "дня"
Проверка: 103 % 100 = 3, это не 11–14, 3 % 10 = 3 — форма «дня». 12 попадает в диапазон 11–14 — «дней».
Количество полных месяцев между датами
Функция ПолныхМесяцев(ДатаНачала, ДатаОкончания)
Месяцев = (Год(ДатаОкончания) - Год(ДатаНачала)) * 12
+ Месяц(ДатаОкончания) - Месяц(ДатаНачала);
Если День(ДатаОкончания) < День(ДатаНачала) Тогда
Месяцев = Месяцев - 1;
КонецЕсли;
Возврат Макс(Месяцев, 0);
КонецФункции
// ПолныхМесяцев('20250315', '20260310') -> 11
Проверка: (2026 − 2025) × 12 + 3 − 3 = 12; день 10 меньше 15, поэтому 12 − 1 = 11.
Рекурсивная функция: факториал
Функция Факториал(Н)
Если Н <= 1 Тогда
Возврат 1;
КонецЕсли;
Возврат Н * Факториал(Н - 1);
КонецФункции
// Факториал(5) -> 120
Рекурсия в 1С работает, но глубина стека ограничена. Для обхода многоуровневых иерархий справочника надёжнее запрос с ИЕРАРХИЯ или цикл, чем глубокая рекурсия.
Асинхронная функция
С платформы 8.3.18 есть асинхронные функции. Объявление начинается словом Асинх, функция возвращает Обещание, а результат получают оператором Ждать:
&НаКлиенте
Асинх Функция ПодтвердитьУдаление()
Ответ = Ждать ВопросАсинх("Удалить строку?", РежимДиалогаВопрос.ДаНет);
Возврат Ответ = КодВозвратаДиалога.Да;
КонецФункции
Вызывающий код тоже должен быть асинхронным и получать результат через Ждать: Если Ждать ПодтвердитьУдаление() Тогда ….
Скорость работы функций: что реально влияет
Само объявление функции на скорость почти не влияет. Тормозят обращения к серверу и базе внутри неё.
- 🚀 Считайте серверные вызовы. Каждый вызов функции
&НаСерверес клиента — это обмен с сервером. Пять вызовов подряд в цикле на клиенте заменяйте одной серверной функцией, которая вернёт структуру со всеми данными. - 🚀
&НаСервереБезКонтекставместо&НаСервере, если данные формы внутри не нужны. Форма с большой табличной частью не будет гоняться на сервер. - 🚀 Не выполняйте запрос внутри цикла. Функцию «получить цену товара», вызванную для 500 строк, перепишите так, чтобы она принимала массив товаров и возвращала соответствие «товар → цена» одним запросом.
- 🚀 Кэш через повторное использование. У общего модуля есть свойство
Повторное использование возвращаемых значенийсо значениями «На время вызова» и «На время сеанса». Функция такого модуля при тех же параметрах не выполняется заново, а отдаёт сохранённый результат. Подходит для редко меняющихся настроек; для данных, которые меняются в течение сеанса, не подходит. - 🚀 Не обращайтесь к реквизиту через точку в цикле.
Товар.Родитель.Наименованиев функции, которая вызывается для каждой строки, — это лишние чтения из базы. Получите нужные поля запросом заранее.
Замерить, какая функция съедает время, позволяет замер производительности в режиме отладки: он показывает время по строкам кода и число вызовов.