Функции во встроенном языке 1С 8.3: параметры, Экспорт, возврат значения и частые ошибки

· 11 мин

Содержание

Функцию в 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, ставка взята по умолчанию

Правила для значений по умолчанию:

  1. Значение — только литерал: число, строка, дата вида '20260101', Истина, Ложь, Неопределено, NULL.
  2. Вызов функции, выражение или константу конфигурации поставить нельзя. Если нужна, например, текущая дата, задайте по умолчанию Неопределено и проверьте внутри: Если Дата = Неопределено Тогда Дата = ТекущаяДатаСеанса(); КонецЕсли;.
  3. Пропущенный параметр в середине списка обозначают пустым местом между запятыми, как в примере с Сумма3.
  4. Удобно ставить необязательные параметры в конец списка: вызовы получаются короче и читаются проще.

Как вернуть несколько значений

Функция возвращает одно значение, но им может быть коллекция. Два рабочих способа:

СпособКогда подходитМинус
Вернуть Структура с полямиРезультатов 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 строк, перепишите так, чтобы она принимала массив товаров и возвращала соответствие «товар → цена» одним запросом.
  • 🚀 Кэш через повторное использование. У общего модуля есть свойство Повторное использование возвращаемых значений со значениями «На время вызова» и «На время сеанса». Функция такого модуля при тех же параметрах не выполняется заново, а отдаёт сохранённый результат. Подходит для редко меняющихся настроек; для данных, которые меняются в течение сеанса, не подходит.
  • 🚀 Не обращайтесь к реквизиту через точку в цикле. Товар.Родитель.Наименование в функции, которая вызывается для каждой строки, — это лишние чтения из базы. Получите нужные поля запросом заранее.

Замерить, какая функция съедает время, позволяет замер производительности в режиме отладки: он показывает время по строкам кода и число вызовов.

Проверьте себя
Функция объявлена как Функция Пересчитать(Таблица). Что изменится у вызывающего кода, если внутри добавить строку в Таблица?
Можно ли задать значением по умолчанию ТекущаяДата()?
Что вернёт функция, если выполнение дошло до КонецФункции без Возврат?

Частые вопросы

Можно ли объявить две функции с одним именем и разными параметрами?
Нет, перегрузки в 1С нет. Используйте одно имя с необязательными параметрами или разные имена.
Сколько раз можно написать Возврат в функции?
Сколько угодно. Выполнение прерывается на первом сработавшем операторе Возврат, остальной код ниже не выполняется.
Как узнать, передан ли необязательный параметр?
Задайте по умолчанию Неопределено и сравнивайте с ним внутри функции. Отличить «не передан» от «передан Неопределено» нельзя.
Можно ли объявить функцию внутри другой функции?
Нет. Вложенных функций и лямбд во встроенном языке нет, все методы объявляются на уровне модуля.
Как вызвать функцию по имени из строки?
Через Вычислить(“ИмяМодуля.ИмяФункции(Параметр)”) на сервере. Строку из пользовательского ввода туда не подставляйте: это выполнение произвольного кода.