← Назад к вопросам

Как в объекте присваивать свойству число и при прочтении этого свойства получать строку?

2.0 Middle🔥 151 комментариев
#JavaScript Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как в объекте присваивать свойству число и при прочтении этого свойства получать строку?

Отличный вопрос! Это возможно несколькими способами, используя геттеры/сеттеры (accessors), Object.defineProperty() или Proxy. Каждый подход имеет свои преимущества.

Способ 1: Геттеры и сеттеры (Getters/Setters)

Это самый простой и читаемый способ:

const person = {
  _age: null, // "приватное" свойство (по соглашению)
  
  set age(value) {
    // При присваивании - сохраняем как число
    this._age = Number(value);
  },
  
  get age() {
    // При чтении - возвращаем как строку
    return String(this._age);
  }
};

person.age = 25;        // Присваиваем число
console.log(person.age); // Получаем строку: "25"
console.log(typeof person.age); // "string"

person.age = "30";      // Можно присвоить строку
console.log(person.age); // Всё равно вернётся строка: "30"
console.log(typeof person.age); // "string"

Способ 2: Object.defineProperty()

Более низкоуровневый, но более контролируемый подход:

const obj = {};

Object.defineProperty(obj, 'count', {
  _value: null,
  
  set(newValue) {
    this._value = Number(newValue);
  },
  
  get() {
    return String(this._value);
  },
  
  enumerable: true,      // показывать в Object.keys()
  configurable: true     // позволить переопределить свойство
});

obj.count = 100;         // Присваиваем число
console.log(obj.count);  // Получаем строку: "100"

Это особенно полезно, если нужен более тонкий контроль:

const config = {};

Object.defineProperty(config, 'port', {
  _port: 3000,
  
  set(value) {
    if (value < 0 || value > 65535) {
      throw new Error('Порт должен быть от 0 до 65535');
    }
    this._port = Number(value);
  },
  
  get() {
    return `PORT_${this._port}`;
  },
  
  enumerable: true
});

config.port = 8080;              // OK
console.log(config.port);        // "PORT_8080"

config.port = 99999;             // Error: Порт должен быть от 0 до 65535

Способ 3: Proxy (самый гибкий)

Proxy позволяет перехватывать операции с объектом:

const handler = {
  set(target, property, value) {
    // При присваивании - сохраняем как число
    target[property] = Number(value);
    return true; // указывает на успешное присваивание
  },
  
  get(target, property) {
    // При чтении - возвращаем как строку
    return String(target[property]);
  }
};

const obj = new Proxy({}, handler);

obj.value = 42;              // Присваиваем число
console.log(obj.value);      // Получаем строку: "42"

Proxy больше контроля позволяет делать. Например, валидация и логирование:

const userProxy = new Proxy(
  { _price: 0 },
  {
    set(target, property, value) {
      // Логируем изменения
      console.log(`Изменение ${property}: ${target[property]} -> ${value}`);
      
      // Валидируем
      if (property === 'price' && value < 0) {
        throw new Error('Цена не может быть отрицательной');
      }
      
      // Сохраняем
      target[`_${property}`] = Number(value);
      return true;
    },
    
    get(target, property) {
      if (property === 'price') {
        // Возвращаем как строку с форматированием
        return `$${target._price.toFixed(2)}`;
      }
      return target[property];
    }
  }
);

userProxy.price = 99.5;       // Логирует: Изменение price: 0 -> 99.5
console.log(userProxy.price); // Получаем: "$99.50"

Способ 4: Класс с приватными полями

Это современный подход с классами:

class User {
  #age = null;  // Приватное поле
  
  set age(value) {
    // При присваивании - валидируем и сохраняем
    if (value < 0 || value > 150) {
      throw new Error('Некорректный возраст');
    }
    this.#age = Number(value);
  }
  
  get age() {
    // При чтении - возвращаем как строку
    return this.#age === null ? 'undefined' : String(this.#age);
  }
}

const user = new User();
user.age = 25;              // Присваиваем число
console.log(user.age);      // Получаем строку: "25"

// Нельзя получить доступ к приватному полю напрямую
// console.log(user.#age); // SyntaxError

Сравнение подходов

Геттеры/сеттеры:

  • Плюсы: Простота, читаемость, стандартный синтаксис
  • Минусы: Нет валидации по умолчанию, нужно писать вручную
  • Когда использовать: Большинство случаев

Object.defineProperty():

  • Плюсы: Больше контроля (enumerable, configurable, writable)
  • Минусы: Менее читаемо
  • Когда использовать: Нужен контроль над атрибутами свойства

Proxy:

  • Плюсы: Максимальная гибкость, работает со всеми свойствами сразу
  • Минусы: Может быть медленнее, сложнее отладить
  • Когда использовать: Нужна динамическая трансформация для многих свойств

Классы с приватными полями:

  • Плюсы: Современный синтаксис, настоящая приватность
  • Минусы: Нужен современный JavaScript (ES2022+)
  • Когда использовать: Новые проекты, TypeScript

Практический пример: API адаптер

Имагина, API возвращает ID как число, а нам нужно работать со строками (для React keys):

class APIResponse {
  #data;
  
  constructor(data) {
    this.#data = data;
  }
  
  // ID: число при присваивании, строка при чтении
  set id(value) {
    this.#data.id = Number(value);
  }
  
  get id() {
    return String(this.#data.id);
  }
  
  // Цена: число при присваивании, строка форматированная при чтении
  set price(value) {
    this.#data.price = parseFloat(value);
  }
  
  get price() {
    return `$${this.#data.price.toFixed(2)}`;
  }
}

const response = new APIResponse({ id: 123, price: 99.5 });

response.id = 456;
console.log(response.id);     // "456" (строка)

response.price = 199.99;
console.log(response.price);  // "$199.99" (форматированная строка)

Когда это использовать

  1. Трансформация типов - конвертация между форматами
  2. Валидация - проверка при присваивании
  3. Логирование - отслеживание изменений
  4. Вычисляемые свойства - зависимые значения
  5. Наследование - контроль над интерфейсом подкласса

Вывод: Для этой задачи больше всего подходят геттеры/сеттеры (просто и элегантно) или Proxy (если нужна максимальная гибкость). Выбор зависит от сложности трансформации и необходимого контроля.

Как в объекте присваивать свойству число и при прочтении этого свойства получать строку? | PrepBro