Приведи пример сложной функции на JavaScript и объясни её логику.

«Приведи пример сложной функции на JavaScript и объясни её логику.» — вопрос из категории Основы программирования, который задают на 10% собеседований QA Тестировщик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Пример — функция глубокого слияния (deep merge) объектов с обработкой специальных типов данных и защитой от циклических ссылок.

/**
 * Рекурсивно глубоко объединяет два и более объектов.
 * @param {Object} target - Целевой объект, в который производится слияние.
 * @param {...Object} sources - Исходные объекты.
 * @returns {Object} - Новый, глубоко объединенный объект.
 */
function deepMerge(target, ...sources) {
  // Если target не объект или null, возвращаем его же (или новый объект)
  if (typeof target !== 'object' || target === null) {
    target = {};
  }

  for (const source of sources) {
    // Пропускаем undefined или null источники
    if (source == null) continue;

    for (const key in source) {
      // Обрабатываем только собственные свойства источника
      if (!source.hasOwnProperty(key)) continue;

      const targetValue = target[key];
      const sourceValue = source[key];

      // Рекурсивное слияние, если оба значения — объекты и не являются массивами
      if (isObject(targetValue) && isObject(sourceValue) && !Array.isArray(sourceValue)) {
        // Защита от циклических ссылок: не мерджим объект с самим собой
        if (sourceValue !== targetValue) {
          target[key] = deepMerge({}, targetValue, sourceValue); // Создаем новый объект для слияния
        }
      } else {
        // Копирование примитивов, массивов, функций и других объектов
        target[key] = cloneValue(sourceValue);
      }
    }
  }
  return target;
}

/** Проверяет, является ли значение объектом (но не null). */
function isObject(value) {
  return typeof value === 'object' && value !== null;
}

/** Глубоко клонирует значение с учетом специальных типов. */
function cloneValue(value) {
  if (Array.isArray(value)) {
    // Клонируем массив, рекурсивно клонируя его элементы
    return value.map(item => cloneValue(item));
  }
  if (value instanceof Date) {
    // Создаем новый объект Date
    return new Date(value.getTime());
  }
  if (value instanceof RegExp) {
    // Клонируем регулярное выражение
    return new RegExp(value.source, value.flags);
  }
  if (isObject(value)) {
    // Для простых объектов — рекурсивный клон
    return deepMerge({}, value);
  }
  // Примитивы (числа, строки, boolean, undefined, функции) возвращаются как есть
  return value;
}

// Пример использования
const defaults = {
  api: {
    url: 'https://api.example.com',
    timeout: 5000,
    headers: { 'User-Agent': 'App' }
  },
  features: ['search', 'filter'],
  created: new Date('2023-01-01')
};

const userConfig = {
  api: {
    timeout: 10000,
    headers: { 'Authorization': 'Bearer token123' }
  },
  features: ['export'], // Массив перезаписывается, а не мерджится!
  retryPolicy: { attempts: 3 }
};

const mergedConfig = deepMerge({}, defaults, userConfig);
console.log(mergedConfig);
/*
{
  api: {
    url: 'https://api.example.com',          // из defaults
    timeout: 10000,                         // перезаписано из userConfig
    headers: {                              // глубоко объединено
      'User-Agent': 'App',
      'Authorization': 'Bearer token123'
    }
  },
  features: ['export'],                     // массив перезаписан
  created: Date('2023-01-01T00:00:00.000Z'), // склонированная дата
  retryPolicy: { attempts: 3 }              // добавлено из userConfig
}
*/

Что делает код сложным:

  1. Рекурсия для обработки вложенных структур.
  2. Различение типов: объекты, массивы, даты, регулярки обрабатываются по-разному.
  3. Защита от циклических ссылок: проверка if (sourceValue !== targetValue).
  4. Иммутабельность: функция не мутирует исходные объекты, создавая новые (deepMerge({}, ...)).
  5. Обработка собственных свойств: hasOwnProperty игнорирует унаследованные свойства из цепочки прототипов.

Альтернатива в современном JS: использование structuredClone() для глубокого клонирования и библиотек вроде Lodash (_.merge()).