Функции во встроенном языке 1С от объявления до вызова
Содержание
Функцию во встроенном языке 1С объявляют ключевым словом Функция, пишут внутри код и возвращают результат оператором Возврат. Минимальный синтаксис — две строки заголовка и тело между ними. Вызывают функцию прямо в выражении: Итого = СуммаСНДС(Цена, 20);.
Главное отличие функции от процедуры — она отдаёт значение, а процедура просто выполняет действия. Если вам нужен результат для дальнейших вычислений, пишите функцию; если нужно что-то сделать (записать документ, вывести сообщение) — процедуру. Ниже — разбор с примерами кода: как объявить функцию, как передать аргументы по ссылке и по значению, что вернуть и как не наступить на типичные грабли с рекурсией и областью видимости.
Функция или процедура: что выбрать под задачу
Оба метода объявляются и устроены почти одинаково, но ведут себя по-разному при вызове.
| Признак | Функция | Процедура |
|---|---|---|
| Ключевое слово | Функция … КонецФункции | Процедура … КонецПроцедуры |
| Возвращает значение | да, обязательно по смыслу | нет |
| Как вызывают | в выражении: Р = Расчёт(А); | оператором: Записать(Док); |
Оператор Возврат | с значением: Возврат Итог; | без значения, для раннего выхода |
Практическое правило: если результат метода нужен в правой части присваивания или внутри условия — это функция. Функцию можно вызвать и как процедуру, проигнорировав результат, а вот процедуру в выражение не поставить — будет ошибка компиляции.
Называйте функции по тому, что они отдают (ПолучитьКурс, ЭтоРабочийДень), а процедуры — по действию (ПровестиДокумент, ОчиститьКэш). По имени сразу видно, ждать результат или нет.
Синтаксис объявления и тело функции
Заголовок состоит из ключевого слова, имени, списка параметров в скобках и необязательного слова Экспорт. Тело — любой код, который заканчивается оператором Возврат.
Функция СуммаСНДС(Цена, Ставка = 20) Экспорт
НДС = Цена * Ставка / 100;
Возврат Цена + НДС;
КонецФункции
Разберём по частям:
Функция— начало объявления, обязательно.СуммаСНДС— имя. Русские и латинские буквы, цифры и подчёркивание; цифра не может быть первой.(Цена, Ставка = 20)— параметры; уСтавказадано значение по умолчанию.Экспорт— делает функцию доступной из других модулей (подробнее ниже).Возврат Цена + НДС;— отдаёт результат и завершает работу.КонецФункции— закрывающее слово, обязательно.
Вызов в коде выглядит так:
Итого = СуммаСНДС(1000); // 1200, ставка взята по умолчанию
ИтогоЛьгота = СуммаСНДС(1000, 10); // 1100
Проверим арифметику первого вызова: НДС = 1000 × 20 / 100 = 200, значит результат 1000 + 200 = 1200. Второй: 1000 × 10 / 100 = 100, результат 1100.
Несколько правил, которые экономят время:
- ⚠️ Объявлять функцию внутри другой функции нельзя — все методы объявляются на уровне модуля.
- Порядок объявления в модуле не важен: функция, описанная ниже, вызывается из метода выше — платформа компилирует весь модуль целиком.
- Если выполнение дошло до
КонецФункции, не встретивВозврат, функция вернётНеопределено. Это частый источник ошибок «значение не определено» у вызывающего кода.
Параметры и передача аргументов
По умолчанию параметры передаются по ссылке: если внутри метода изменить значение параметра, изменение увидит и вызывающий код.
Процедура Удвоить(Число)
Число = Число * 2;
КонецПроцедуры
Х = 5;
Удвоить(Х);
// теперь Х равно 10
Чтобы защитить переданную переменную от изменений, поставьте перед параметром ключевое слово Знач — передача пойдёт по значению, метод получит копию:
Процедура Удвоить(Знач Число)
Число = Число * 2; // снаружи Х останется 5
КонецПроцедуры
Здесь есть тонкость, на которой спотыкаются даже опытные разработчики. Знач защищает только от переприсваивания самой переменной. Если параметр — объект-ссылка (массив, структура, таблица значений), то изменение его содержимого видно снаружи даже со Знач:
Процедура ДобавитьСтроку(Знач Список)
Список.Добавить("новый"); // элемент попадёт в исходный массив!
КонецПроцедуры
Числа, строки, даты и булево при Знач копируются полностью, а объекты остаются общими — Знач лишь запрещает заменить сам объект другим. Если нужна настоящая независимая копия коллекции, делайте её явно, например через Скопировать() у таблицы значений.
Значения по умолчанию
Параметр со значением по умолчанию можно не указывать при вызове. Значение по умолчанию задаётся только литералом-константой (число, строка, Истина, Ложь, Неопределено, дата), выражение писать нельзя.
Функция Приветствие(Имя, Текст = "Здравствуйте") Экспорт
Возврат Текст + ", " + Имя + "!";
КонецФункции
Все параметры со значениями по умолчанию должны идти в конце списка. Нельзя задать значение первому параметру и оставить без значения второй. Если нужно пропустить средний аргумент при вызове, оставьте его пустым: Метод(А, , В).
Возврат значения и типы данных
Встроенный язык 1С — с динамической типизацией: у переменных нет объявленного типа, и функция может вернуть значение любого типа — число, строку, ссылку на объект, массив, Неопределено.
Функция НайтиКонтрагента(ИНН) Экспорт
Запрос = Новый Запрос;
// … настройка и выполнение запроса …
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
Возврат Выборка.Ссылка; // возвращаем ссылку на справочник
КонецЕсли;
Возврат Неопределено; // ничего не нашли
КонецФункции
Хороший тон — чтобы функция всегда возвращала предсказуемый тип. Проверяйте результат у вызывающей стороны:
Контрагент = НайтиКонтрагента("7701234567");
Если Контрагент = Неопределено Тогда
Сообщить("Контрагент не найден");
Возврат;
КонецЕсли;
Когда нужно вернуть несколько значений, не заводите выходные параметры через ссылку — соберите результат в структуру. Код вызывающей стороны получится чище:
Функция РазобратьФИО(ПолноеИмя) Экспорт
Части = СтрРазделить(ПолноеИмя, " ", Ложь);
Результат = Новый Структура("Фамилия, Имя, Отчество");
Результат.Фамилия = ?(Части.Количество() > 0, Части[0], "");
Результат.Имя = ?(Части.Количество() > 1, Части[1], "");
Результат.Отчество = ?(Части.Количество() > 2, Части[2], "");
Возврат Результат;
КонецФункции
Оператор Возврат немедленно прекращает выполнение функции, поэтому его удобно использовать для ранних выходов: проверили условие в начале — вышли, не уводя основную логику в глубокую вложенность Если.
Экспорт, область видимости и директивы компиляции
Область видимости определяет, откуда функция доступна.
- Без
Экспорт— функция видна только внутри своего модуля. Это служебные, вспомогательные методы. - С
Экспорт— функция становится частью программного интерфейса модуля и доступна снаружи: из других модулей, из общих модулей, из форм.
Переменные тоже имеют область видимости:
- Локальная переменная объявляется (или возникает при первом присваивании) внутри метода и живёт только во время его работы.
- Переменная модуля объявляется оператором
Перемв начале модуля и доступна во всех его методах; сПерем … Экспорт— и снаружи.
Перем КэшКурсов Экспорт; // переменная модуля, видна извне
Переиспользуемые функции, которые нужны в разных местах конфигурации, принято выносить в общие модули. Там объявляют метод с Экспорт и вызывают его из форм, документов и других общих модулей через точку: ОбщийМодуль.ИмяМетода(…). Для общего модуля в его свойствах задают флажки «Сервер», «Клиент (управляемое приложение)» и «Вызов сервера» — они определяют, откуда метод можно вызвать. Не складывайте всё в один огромный общий модуль: группируйте функции по смыслу (работа с датами, строками, расчёты налогов) — так их проще искать и поддерживать.
В управляемом приложении у методов есть директивы компиляции — они задают, на клиенте или на сервере выполняется код. Директиву пишут строкой выше объявления:
&НаКлиенте— метод исполняется на клиенте, видит данные формы и может работать с интерактивными диалогами.&НаСервере— исполняется на сервере в контексте формы, имеет доступ к базе данных.&НаСервереБезКонтекста— на сервере, но без передачи данных формы; быстрее, подходит для вспомогательных расчётов.&НаКлиентеНаСервереБезКонтекста— один код, доступный и клиенту, и серверу.
&НаСервереБезКонтекста
Функция ПолучитьОстаток(Номенклатура, Склад) Экспорт
// обращение к регистру — только на сервере
КонецФункции
Запросы к базе, чтение регистров и справочников возможны только на сервере. Если клиентский метод пытается обратиться к базе напрямую — будет ошибка компиляции. Разносите код по директивам осознанно.
Рекурсия и её ограничения
Функция может вызывать сама себя — это рекурсия. Классический пример — факториал:
Функция Факториал(Н)
Если Н <= 1 Тогда
Возврат 1;
КонецЕсли;
Возврат Н * Факториал(Н - 1);
КонецФункции
У рекурсии обязательно должно быть условие выхода (здесь Н <= 1), иначе функция будет вызывать себя бесконечно. Глубина вложенности вызовов в платформе ограничена: при слишком большом числе уровней вы получите ошибку о превышении допустимой глубины вложенности, а не тихое зависание.
Поэтому рекурсия хороша для действительно древовидных данных — обход групп справочника, разузлование спецификаций, разбор вложенных структур. Для линейных задач (пройти большой список, посчитать сумму) замените рекурсию обычным циклом — он не копит стек вызовов и работает стабильнее.
// то же, что рекурсивный факториал, но циклом
Функция Факториал(Н)
Результат = 1;
Для Сч = 2 По Н Цикл
Результат = Результат * Сч;
КонецЦикла;
Возврат Результат;
КонецФункции
Перед рекурсивным обходом иерархического справочника всегда ограничивайте уровень вложенности или ведите список уже посещённых элементов — иначе зацикленная иерархия в данных уведёт обход в бесконечность.
Практики, которые упростят поддержку кода
Рабочий код живёт годами, и читают его чаще, чем пишут. Несколько привычек делают функции понятнее и надёжнее.
- 🎯 Одна функция — одна задача. Если метод и считает, и записывает, и выводит сообщения — разбейте его. Короткую функцию проще отладить и переиспользовать.
- 🏷 Осмысленные имена.
РассчитатьПени, а неОбработка2. Для функций-проверок удобны имена-вопросы:ЭтоПустаяСтрока,ДокументПроведён. - 🛡 Входные параметры — со
Знач, если метод их не меняет. Так явно видно намерение, и случайное изменение не уйдёт к вызывающему коду. - 📦 Несколько результатов — через структуру, а не через ссылочные параметры. Вызов читается проще.
- 🚪 Ранний выход вместо глубоких
Если. Проверили некорректные данные в начале, сделалиВозврат— основная логика остаётся без лишних отступов. - 🧩 Экспортируйте только то, что нужно снаружи. Остальные методы держите без
Экспорт— это скрывает внутреннюю кухню модуля.
Избегайте побочных эффектов в функциях. Функция, которая кроме расчёта ещё и меняет данные в базе или глобальные переменные, ведёт себя непредсказуемо: вызвали «просто посчитать» — а в базе что-то изменилось.
Для часто вызываемых функций думайте о производительности: не открывайте запрос внутри цикла по тысячам строк, а получайте данные одним запросом заранее. Кэшируйте результат в переменной модуля или в параметрах сеанса, если он не меняется в рамках операции.
Частые вопросы
Можно ли вызвать функцию, объявленную ниже по тексту модуля?
Чем `Знач` отличается от обычной передачи параметра?
Знач параметр передаётся по ссылке: изменение внутри метода видит вызывающий код. Со Знач метод получает копию значения. Но для объектов (массив, структура, таблица значений) Знач защищает только от переприсваивания самой переменной, а изменение содержимого объекта всё равно видно снаружи.Как вернуть из функции несколько значений?
Почему функция возвращает `Неопределено`, хотя я ничего такого не писал?
КонецФункции, не встретив оператор Возврат со значением, — например, нужная ветка Если не сработала. Убедитесь, что Возврат есть на всех путях выполнения.Что означают директивы `&НаКлиенте` и `&НаСервере` перед функцией?
&НаСервере даёт доступ к базе данных, &НаКлиенте работает с формой и интерактивными диалогами. Запросы к базе возможны только на сервере.