Перейти к содержанию
  • Лента
  • Категории
  • Последние
  • Метки
  • Популярные
  • Пользователи
  • Группы
Свернуть
exlends
Категории
  1. Главная
  2. Категории
  3. Языки программирования
  4. JavaScript
  5. Object.entries + fromEntries против for: безопасная фильтрация конфига

Object.entries + fromEntries против for: безопасная фильтрация конфига

Запланировано Прикреплена Закрыта Перенесена JavaScript
object.entriesбезопасностьконфиг
1 Сообщения 1 Постеры 2 Просмотры
  • Сначала старые
  • Сначала новые
  • По количеству голосов
Ответить
  • Ответить, создав новую тему
Авторизуйтесь, чтобы ответить
Эта тема была удалена. Только пользователи с правом управления темами могут её видеть.
  • hannadevH Не в сети
    hannadevH Не в сети
    hannadev
    написал отредактировано
    #1

    Когда ты подтягиваешь конфиг с API, хочется быстро отфильтровать лишнее и не получить проблемы с прототипом. Старый подход с циклом for — это граница между “работает” и “хак”. Современный способ через Object.entries и Object.fromEntries выглядит элегантнее и безопаснее. Давай разбираться, в чём на самом деле фишка.

    Вещи, которые обычно ломают новичков: забывают про наследование из прототипа, создают утечки памяти через случайные мутации, пишут столько условий, что код становится нечитаемым. Здесь речь именно про эти граблями.

    Проблема старого подхода: цикл for и его сюрпризы

    Цикл for — это универсальный солдат, но он универсален ровно настолько, насколько ты внимателен. Когда ты перебираешь объект через for…in, браузер не только проходит по собственным свойствам, но и лезет в прототип. Это может привести к неожиданным свойствам в результате, если у объекта есть наследованные члены.

    Ещё классика: если ты мутируешь исходный объект или создаёшь новый через литерал {}, ты неявно наследуешься от Object.prototype. И если где-то в коде кто-нибудь добавит property на Object.prototype (чего делать не стоит, но бывает), оно внезапно появится везде. Вот такие сюрпризы в боевом коде стоят ночных разборов.

    // Опасный подход
    const config = { api: 'prod', debug: false, timeout: 5000 };
    const filtered = {};
    
    for (const key in config) {
      // Если в прототипе есть что-то наследованное — попадет сюда
      if (config[key] !== null && config[key] !== undefined) {
        filtered[key] = config[key];
      }
    }
    
    // Если Object.prototype.inherited = 'value' — попадет в filtered!
    

    Вопрос очень простой: зачем усложнять себе жизнь, если есть методы, которые работают только с собственными свойствами?

    Object.entries: переход от объекта к массиву пар

    Object.entries() делает одно, но делает хорошо: возвращает массив пар [ключ, значение] исключительно для собственных свойств объекта. Прототип остаётся за бортом. Это чистая структура, с которой можно работать как с массивом - фильтровать, маппить, сортировать.

    Что даёт такой подход? Во-первых, декларативность: ты видишь, что нужно делать, прямо в коде. Во-вторых, неизменяемость по умолчанию: исходный объект остаётся нетронутым, потому что ты работаешь с копией. В-третьих, стандартные методы массива работают так, как ты их знаешь, без подвохов.

    const serverConfig = {
      host: 'localhost',
      port: 3000,
      debug: true,
      secret: null,
      timestamp: undefined
    };
    
    const entries = Object.entries(serverConfig);
    console.log(entries);
    // [
    //   ['host', 'localhost'],
    //   ['port', 3000],
    //   ['debug', true],
    //   ['secret', null],
    //   ['timestamp', undefined]
    // ]
    
    // Фильтруем: убираем null и undefined
    const filtered = entries.filter(([key, value]) => value != null);
    console.log(filtered);
    // [
    //   ['host', 'localhost'],
    //   ['port', 3000],
    //   ['debug', true]
    // ]
    

    Видишь разницу? Нет никаких скрытых свойств из прототипа, нет проверок на hasOwnProperty(). Просто берёшь то, что есть, и работаешь.

    Object.fromEntries: замыкаем круг

    Object.fromEntries() — это обратная сторона медали. Если Object.entries() разворачивает объект в массив пар, то fromEntries() собирает его обратно. Вот тут и рождается красивая паттерна: преобразование - операция - обратное преобразование.

    Зачем это нужно? После фильтрации или маппинга у тебя остаётся массив пар. Чтобы вернуться к объекту и дальше его использовать в коде, нужна reverse-операция. А Object.fromEntries() гарантирует, что результат будет чистый объект без наследования, без лишних ссылок.

    // Фильтруем и возвращаем объект
    const config = { api: 'https://api.example.com', timeout: 5000, debug: null, retry: undefined };
    
    const cleaned = Object.fromEntries(
      Object.entries(config).filter(([key, value]) => value != null)
    );
    
    console.log(cleaned);
    // { api: 'https://api.example.com', timeout: 5000 }
    
    // Или трансформируем значения
    const transformed = Object.fromEntries(
      Object.entries(config).map(([key, value]) => [
        key.toUpperCase(),
        typeof value === 'string' ? value.toUpperCase() : value
      ])
    );
    
    console.log(transformed);
    // { API: 'HTTPS://API.EXAMPLE.COM', TIMEOUT: 5000, DEBUG: null, RETRY: undefined }
    

    Этот паттерн работает не только с объектами. Object.fromEntries() принимает любой iterable - Map, Array, даже генератор. Это даёт гибкость при работе с разными источниками данных.

    Сравнение подходов: что выбрать?

    Вот здесь важно понять, какой способ подходит под конкретную задачу. Они решают её по-разному, с разными побочными эффектами.

    Подход Плюсы Минусы Когда использовать
    for…in Простой синтаксис, привычный Нужна проверка hasOwnProperty(), может поймать наследованные свойства, мутирует исходный объект если не осторожен Редко, если вообще нужен
    Object.entries() + filter/map + fromEntries() Безопасно от прототипа, декларативно, неизменяемо, стандартные методы массива Немного медленнее на огромных объектах (но это не критично), нужна поддержка ES2019 Всегда, это современный стандарт
    Object.keys() + цикл Избегаешь наследования, понятнее for…in Нужно создавать новый объект, всё равно требует условий Если нужна максимальная производительность (очень редко)

    Звучит как реклама Object.entries, но это работает. Исключения есть: если ты работаешь с древним Internet Explorer (давай, кто-то же его ещё использует?) или нужна микрооптимизация в критичном месте. В остальных случаях это стандарт, от которого не стоит отскакивать.

    Реальный случай: парсим конфиг с API

    Представим ситуацию: пришёл ответ с API, там конфиг приложения. Часть полей пустые, часть не нужны на фронте, часть нужно преобразовать. Типичная задача для production-среды.

    // Ответ от API - сырой, как есть
    const apiResponse = {
      appName: 'MyApp',
      apiEndpoint: 'https://api.prod.com',
      deprecatedField: null,
      internalSecret: '12345',
      userTimeout: 30000,
      adminPanel: undefined,
      theme: 'dark',
      experimentalFeature: null
    };
    
    // Вариант 1: Убираем null и undefined
    const safeConfig = Object.fromEntries(
      Object.entries(apiResponse).filter(([key, value]) => value != null)
    );
    
    console.log(safeConfig);
    // { appName: 'MyApp', apiEndpoint: '...', internalSecret: '12345', userTimeout: 30000, theme: 'dark' }
    
    // Вариант 2: Фильтруем по whitelist - безопаснее для security
    const allowedFields = ['appName', 'apiEndpoint', 'userTimeout', 'theme'];
    const whitelisted = Object.fromEntries(
      Object.entries(apiResponse).filter(([key]) => allowedFields.includes(key))
    );
    
    console.log(whitelisted);
    // { appName: 'MyApp', apiEndpoint: '...', userTimeout: 30000, theme: 'dark' }
    
    // Вариант 3: Преобразуем значения в нужный формат
    const processed = Object.fromEntries(
      Object.entries(apiResponse)
        .filter(([key, value]) => value != null)
        .map(([key, value]) => {
          // Преобразуем таймауты в секунды, если это нужно
          if (key.includes('Timeout') && typeof value === 'number') {
            return [key, Math.floor(value / 1000)];
          }
          return [key, value];
        })
    );
    
    console.log(processed);
    // { appName: 'MyApp', apiEndpoint: '...', userTimeout: 30, theme: 'dark', ... }
    

    Видишь, как это выглядит? Без петель, без условий в конце, весь трансформ в одной цепочке. И главное - результат гарантированно не содержит ничего, кроме того, что явно там оказалось. Никаких скрытых наследований, никаких утечек.

    Производительность: стоит ли переживать?

    Обычный вопрос: “Но ведь Object.entries() создаёт новый массив? Не медленнее ли?” Да, создаёт. Но в реальных приложениях это не имеет значения, если ты не фильтруешь объекты с миллионами свойств каждый кадр.

    Если конфиг пришёл с API, это обычно десятки полей максимум. Даже сотни - это ничто для JS-движков. Где это действительно может быть проблемой - это когда ты делаешь это на каждый обновления UI, миллион раз в цикле. Но в таких случаях проблема не в Object.entries(), а в архитектуре, которая это позволяет.

    Помни: оптимизируй то, что действительно медленно. Профилируй перед тем, как что-то оптимизировать. Object.entries() достаточно быстрый для 99% случаев.

    // Если ты действительно параноик по производительности
    // (что обычно не нужно), можно использовать Object.keys()
    const config = { a: 1, b: 2, c: null };
    const filtered = {};
    
    for (const key of Object.keys(config)) {
      const value = config[key];
      if (value != null) {
        filtered[key] = value;
      }
    }
    
    // Это немного быстрее, чем entries() + fromEntries(), но читаемость хуже
    

    Что ещё нужно знать

    Несколько моментов, которые часто упускают:

    • Object.entries() не включает символические ключи - если у тебя есть символы как ключи, они останутся невидимыми. Это обычно хорошо, но иногда может быть неожиданно.
    • Порядок свойств гарантирован - в современном JS порядок свойств в объекте совпадает с порядком их добавления (для строковых ключей). Object.entries() сохраняет этот порядок.
    • fromEntries() создаёт новый объект - это значит, что ссылка на исходный объект теряется. Если тебе это важно, нужно переписать логику.
    • Map и Object.fromEntries() - друзья - ты можешь перевести Map в объект и обратно, это работает отлично и часто решает проблемы с передачей данных между системами.
    // Пример с Map
    const configMap = new Map([
      ['host', 'localhost'],
      ['port', 3000]
    ]);
    
    const configObject = Object.fromEntries(configMap);
    console.log(configObject); // { host: 'localhost', port: 3000 }
    
    // И обратно
    const backToMap = new Map(Object.entries(configObject));
    console.log(backToMap); // Map(2) { 'host' => 'localhost', 'port' => 3000 }
    

    Когда это экономит время и нервы

    Основная ценность этого подхода не в экономии строк кода. Она в том, что ты точно знаешь, что получишь. Нет скрытых наследований, нет побочных эффектов, нет утечек памяти из-за случайных ссылок на данные. Это особенно важно, когда конфиг приходит извне - с API, конфиг-файла, от пользователя.

    Второе - это читаемость для коллег. Когда разработчик смотрит на Object.entries().filter().map(), он сразу понимает, что тут фильтруется и трансформируется. Цикл for с кучей условий требует больше внимания, чтобы разобраться, что на самом деле происходит.

    Третье - это устойчивость к багам. Если ты по ошибке добавишь что-то на Object.prototype, это не сломает твой код. Если кто-то из коллег забудет hasOwnProperty() в цикле, это не будет твоя проблема.

    Получается, что Object.entries + fromEntries - это не про красоту кода, а про его надёжность.

    1 ответ Последний ответ
    0

    Здравствуйте! Похоже, вас заинтересовала эта беседа, но у вас ещё нет аккаунта.

    Надоело каждый раз пролистывать одни и те же посты? Зарегистрировав аккаунт, вы всегда будете возвращаться на ту же страницу, где были раньше, и сможете выбирать, получать ли уведомления о новых ответах (по электронной почте или в виде push-уведомлений). Вы также сможете сохранять закладки и ставить лайки постам, чтобы выразить свою благодарность другим участникам сообщества.

    С вашими комментариями этот пост мог бы стать ещё лучше 💗

    Зарегистрироваться Войти

    Категории

    • Главная
    • Новости
    • Фронтенд
    • Бекенд
    • Языки программирования

    Контакты

    • Сотрудничество
    • info@exlends.com

    © 2024 - 2026 ExLends, Inc. Все права защищены.

    Политика конфиденциальности
    • Войти

    • Нет учётной записи? Зарегистрироваться

    • Войдите или зарегистрируйтесь для поиска.
    • Первое сообщение
      Последнее сообщение
    0
    • Лента
    • Категории
    • Последние
    • Метки
    • Популярные
    • Пользователи
    • Группы