Как устроены переменные во встроенном языке 1С:Предприятия 8.3
Содержание
Переменные в 1С бывают трёх видов по области видимости: локальные (внутри процедуры или функции), переменные модуля (объявлены через Перем в начале модуля) и глобальные (экспортные переменные модуля приложения). Тип у переменной в 1С не фиксируется: тип есть у значения, а переменная может хранить сначала число, потом строку, потом ссылку на справочник.
Объявить переменную можно двумя способами: явно, оператором Перем Имя;, или неявно, первым присваиванием Имя = 0;. Всё, что описано ниже, относится к встроенному языку платформы 8.3 и не зависит от конфигурации: правила одинаковы в Бухгалтерии, ЗУП, УТ и в собственной разработке.
Динамическая типизация: переменная без типа
В 1С нет объявлений вида «целое» или «строка длиной 50». Переменная — это просто имя, к которому привязано значение. Тип определяется в момент присваивания и может смениться в любой строке кода.
Значение = 100; // Число
Значение = "сто"; // теперь Строка
Значение = ТекущаяДата(); // теперь Дата
Компилятор такой код пропустит. Ошибка, если она есть, проявится при выполнении: например, при попытке сложить дату со строкой.
Чтобы узнать, что лежит в переменной, используют две функции:
- 🔍
ТипЗнч(Значение)— возвращает тип текущего значения; - 🔍
Тип("Число")— возвращает тип по его имени, для сравнения.
Если ТипЗнч(Значение) = Тип("Число") Тогда
Значение = Значение * 2;
ИначеЕсли ТипЗнч(Значение) = Тип("СправочникСсылка.Номенклатура") Тогда
Сообщить("Передан товар");
КонецЕсли;
Правила имён
- ✅ Имя состоит из букв, цифр и символа подчёркивания.
- ✅ Начинается с буквы или подчёркивания, не с цифры.
- ✅ Регистр не важен:
сумма,СуммаиСУММА— одна и та же переменная. - ❌ Нельзя использовать зарезервированные слова:
Если,Цикл,Новый,Истинаи другие.
Допустимы и русские, и латинские имена. В типовых конфигурациях принят «верблюжий» стиль по-русски: СуммаДокумента, ЕстьОшибки, СтрокаТабличнойЧасти.
Имя булевой переменной удобно начинать с «Есть», «Это», «Нужно»: ЭтоНовый, НужноПерезаписать. Условие Если НужноПерезаписать Тогда читается без комментария.
Чему равна переменная без значения
Переменная, объявленная через Перем и ещё не получившая значения, содержит Неопределено. Неявно объявленная переменная без присваивания не существует вовсе: обращение к ней в выражении — ошибка компиляции.
Простые типы данных в 1С
Простые (примитивные) типы хранят одно значение и копируются при присваивании. Если Б = А, а потом А изменилась, Б останется прежней.
| Тип | Пример литерала | Что важно знать |
|---|---|---|
| Число | 150, -3.75 | Дробная часть отделяется точкой, не запятой |
| Строка | "ООО ""Ромашка""" | Кавычка внутри строки удваивается |
| Дата | '20261007', '20261007153000' | Хранит дату и время с точностью до секунды |
| Булево | Истина, Ложь | Результат любого сравнения |
| Неопределено | Неопределено | «Значения нет» в языке |
| Null | NULL | «Значения нет» в результатах запроса |
| Тип | Тип("Строка") | Значение, описывающее сам тип |
Числа
Деление целых даёт дробь: 7 / 2 вернёт 3.5. Целую часть берут функцией Цел(), остаток — оператором %: 7 % 2 = 1. Округление — Окр(Число, 2).
Строки
Строки складываются оператором +. Многострочный текст пишут с символом | в начале каждой следующей строки:
Текст = "Первая строка
|Вторая строка";
Число со строкой лучше не склеивать напрямую: результат зависит от того, что стоит слева, а число получит разделитель групп разрядов. Надёжнее так:
Номер = 12500;
Текст = "Заказ № " + Формат(Номер, "ЧГ=0"); // "Заказ № 12500"
Текст = СтрШаблон("Заказ № %1", Формат(Номер, "ЧГ=0"));
Даты
Дата в 1С — это момент времени. Прибавление числа добавляет секунды, а разность двух дат тоже возвращается в секундах:
Завтра = ТекущаяДата() + 86400; // 24 * 60 * 60 = 86400 секунд
ДнейМежду = (ДатаОкончания - ДатаНачала) / 86400;
Пустая дата — '00010101'. Проверять её удобнее функцией ЗначениеЗаполнено(), которая вернёт Ложь для пустой даты, нуля, пустой строки, Неопределено и пустой ссылки.
Неопределено и Null — не одно и то же
- 🔸
Неопределеновозникает в коде: переменная без значения, отсутствующий ключ в соответствии, функция безВозврат. - 🔸
NULLприходит из запроса: левое соединение не нашло пары, реквизит есть не у всех типов составного поля.
Сравнение Значение = Неопределено для NULL вернёт Ложь. Для выборки из запроса проверяйте Значение = NULL или избавляйтесь от него в самом запросе функцией ЕСТЬNULL().
Ссылочные типы и объекты метаданных
Каждый справочник, документ, перечисление, план счетов и план видов характеристик порождает в языке свои типы. Для справочника «Номенклатура» это, в частности:
- 📌
СправочникСсылка.Номенклатура— ссылка на элемент; - 📌
СправочникОбъект.Номенклатура— сам элемент, загруженный для изменения; - 📌
СправочникМенеджер.Номенклатура— менеджер, доступный какСправочники.Номенклатура.
Ссылка и объект — разные вещи
Ссылка — лёгкое значение, по сути указатель на запись в базе. Её можно хранить, сравнивать, передавать с сервера на клиент. Изменить данные через ссылку нельзя.
Товар = Справочники.Номенклатура.НайтиПоКоду("00-00001");
Если НЕ Товар.Пустая() Тогда
ТоварОбъект = Товар.ПолучитьОбъект();
ТоварОбъект.Комментарий = "Проверено";
ТоварОбъект.Записать();
КонецЕсли;
Если элемент не найден, НайтиПоКоду() вернёт пустую ссылку. Её же даёт Справочники.Номенклатура.ПустаяСсылка().
Обращение через точку к реквизиту ссылки (Товар.Артикул) читает данные из базы. В цикле по тысяче строк это тысяча обращений. Нужны реквизиты многих объектов — получайте их одним запросом.
Почему объект нельзя передать на клиент
Значения, которые уходят с сервера на клиент, должны сериализоваться. Ссылки, простые типы, структуры и массивы из них передаются. СправочникОбъект и ДокументОбъект — нет: на клиенте с ними работают через данные формы.
Где видна переменная: локальные, модульные и глобальные
Область видимости определяется местом объявления.
| Вид | Где объявлена | Где доступна | Сколько живёт |
|---|---|---|---|
| Локальная | Внутри процедуры: Перем или присваиванием | Только в этой процедуре | До выхода из процедуры |
| Переменная модуля | Перем в начале модуля | Во всех процедурах модуля | Пока жив экземпляр модуля |
| Экспортная переменная модуля | Перем Имя Экспорт; | Снаружи, через объект или форму | Пока жив объект |
| Глобальная | Перем Имя Экспорт; в модуле управляемого приложения | Во всех клиентских модулях | Весь сеанс на клиенте |
Порядок объявления в модуле
Модуль читается сверху вниз в строгом порядке: раздел объявления переменных, затем процедуры и функции, затем основная программа (операторы вне процедур). Перем после первой процедуры — синтаксическая ошибка.
Перем СчетчикОбработанных; // переменная модуля
Перем РежимЗагрузки Экспорт; // видна снаружи
Процедура ОбработатьСтроку(Строка)
СчетчикОбработанных = СчетчикОбработанных + 1;
КонецПроцедуры
СчетчикОбработанных = 0; // основная программа: инициализация
Внутри процедуры Перем тоже ставится до первого оператора.
Ловушка с одинаковыми именами
Если в модуле объявлена переменная Итог, то строка Итог = 0; внутри любой процедуры этого модуля изменит модульную переменную, а не создаст локальную. Отсюда «странные» значения, которые переживают вызов процедуры. Локальным переменным давайте имена, которые не пересекаются с модульными.
В каких модулях переменные модуля недоступны
В общих модулях раздела объявления переменных нет. Хранить там состояние между вызовами не получится. Для кэша на сервере используют общий модуль с повторным использованием возвращаемых значений или параметры сеанса.
Глобальные переменные на клиенте и на сервере
Экспортная переменная модуля управляемого приложения доступна всем клиентским модулям: формам, общим клиентским модулям. На сервере такого механизма нет. Серверные вызовы могут выполняться разными рабочими процессами, поэтому общее состояние сеанса на сервере хранят в параметрах сеанса (ПараметрыСеанса.ТекущийПользователь в типовых).
Переменные модуля формы
В модуле формы перед Перем ставится директива компиляции:
&НаКлиенте
Перем КэшЦен; // живёт, пока открыта форма
Серверная переменная модуля формы не сохраняет значение между серверными вызовами. Если в одном &НаСервере-методе заполнить её, а в другом прочитать, там будет Неопределено. Данные между вызовами храните в реквизитах формы.
Экспортная переменная модуля объекта
Это рабочий приём для передачи флага в обработчики записи:
// модуль объекта документа
Перем ПропуститьПроверкуОстатков Экспорт;
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
Если ПропуститьПроверкуОстатков = Истина Тогда
Возврат;
КонецЕсли;
// проверка остатков
КонецПроцедуры
Снаружи: ДокОбъект.ПропуститьПроверкуОстатков = Истина; перед ДокОбъект.Записать(РежимЗаписиДокумента.Проведение). Для той же задачи у любого объекта есть встроенная структура ДополнительныеСвойства, и в типовых конфигурациях чаще пользуются ей: не нужно менять модуль объекта.
Параметры процедур и функций: по ссылке и по значению
Параметр — это локальная переменная, которая получает значение при вызове. По умолчанию параметры передаются по ссылке: изменение внутри процедуры меняет переменную вызывающего кода. Ключевое слово Знач включает передачу по значению.
Процедура Увеличить(Число1, Знач Число2)
Число1 = Число1 + 1;
Число2 = Число2 + 1;
КонецПроцедуры
А = 5;
Б = 5;
Увеличить(А, Б);
// А = 6, Б = 5
Знач и коллекции
С массивами и структурами Знач защищает только саму переменную, а не содержимое. Процедура получает копию ссылки на ту же коллекцию:
Процедура Дополнить(Знач Список)
Список.Добавить(1); // исходный массив изменится
Список = Новый Массив; // а эта замена снаружи не видна
КонецПроцедуры
Нужна независимая копия — создайте её явно, например через Новый ФиксированныйМассив(Список) для чтения или перебором в новый массив.
Значения по умолчанию
Необязательные параметры получают значение по умолчанию в объявлении. Они идут после обязательных:
Функция СуммаСНДС(Сумма, Ставка = 20)
Возврат Окр(Сумма * (100 + Ставка) / 100, 2);
КонецФункции
СуммаСНДС(1000); // 1200
СуммаСНДС(1000, 10); // 1100
В серверных методах, которые вызываются с клиента, ставьте Знач у параметров, которые не нужно возвращать. Платформа тогда не передаёт их обратно на клиент, и вызов становится легче.
Коллекции: Массив, Структура, Соответствие и таблицы
Когда одного значения мало, переменная хранит коллекцию. Выбор зависит от того, как вы будете искать данные.
| Коллекция | Доступ | Когда брать |
|---|---|---|
Массив | По индексу с 0 | Список однотипных значений |
Структура | По имени ключа через точку | Набор именованных параметров |
Соответствие | По любому ключу, в том числе ссылке | Словарь «товар → цена» |
СписокЗначений | По индексу, есть пометки и представления | Выбор из списка в интерфейсе |
ТаблицаЗначений | Строки и колонки, поиск, свёртка | Данные из запроса, расчёты |
ДеревоЗначений | Иерархия строк | Группы и подчинённые элементы |
Примеры
Товары = Новый Массив;
Товары.Добавить("Бумага");
Товары.Добавить("Тонер");
Первый = Товары[0]; // "Бумага"
Всего = Товары.Количество(); // 2
Параметры = Новый Структура("Организация, Дата", Организация, ТекущаяДата());
Параметры.Вставить("Склад", Склад);
Цены = Новый Соответствие;
Цены.Вставить(Товар, 350);
Цена = Цены[Товар]; // 350 или Неопределено, если ключа нет
Таблица = Новый ТаблицаЗначений;
Таблица.Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число"));
Структура и Соответствие ведут себя по-разному при отсутствии ключа
- 🔸
Цены[Товар]для отсутствующего ключа спокойно вернётНеопределено. - 🔸
Параметры.Складдля отсутствующего ключа вызовет ошибкуПоле объекта не обнаружено (Склад).
Безопасный способ прочитать структуру — метод Свойство():
Склад = Неопределено;
Если Параметры.Свойство("Склад", Склад) Тогда
// ключ есть, значение уже в переменной Склад
КонецЕсли;
Фиксированные коллекции
ФиксированныйМассив, ФиксированнаяСтруктура и ФиксированноеСоответствие нельзя изменить после создания. Их удобно возвращать из функций с повторным использованием: вызывающий код не испортит кэш.
Частые ошибки с переменными и как их избежать
Ещё два правила, которые снимают половину отладки:
- Инициализируйте счётчики и суммы явно перед циклом:
Итого = 0;. - Не используйте одну переменную под разные смыслы. Пусть
Значениене будет сначала суммой, а потом строкой отчёта.