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

Как пошарить код между двумя участками кода без window?

2.2 Middle🔥 192 комментариев
#JavaScript Core

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

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

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

Способы шаринга кода между модулями без использования window

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

1. ES6 модули (экспорт/импорт)

Это наиболее правильный и современный способ:

// utils/math.js
export function add(a, b) {
  return a + b;
}

export function multiply(a, b) {
  return a * b;
}

export const PI = 3.14159;

// Другой файл
import { add, multiply, PI } from './utils/math.js';

console.log(add(2, 3));      // 5
console.log(multiply(4, 5));  // 20
console.log(PI);              // 3.14159

Преимущества:

  • Явная зависимость (видно, откуда приходит функция)
  • Static analysis (линтеры и бандлеры могут оптимизировать)
  • Tree shaking (неиспользуемый код удаляется)
  • IDE автодополнение работает идеально

2. Именованные экспорты vs Default экспорт

// Правильный паттерн: несколько функций
// utils/api.js
export async function fetchUser(id) { }
export async function fetchPosts(userId) { }
export async function saveUser(user) { }

// Использование
import { fetchUser, fetchPosts } from './utils/api.js';

// ПЛОХО: default экспорт для функций
// utils/api.js
export default async function fetchUser(id) { }

// Это скрывает название и затрудняет поиск
import someFunction from './utils/api.js'; // Что это функция?

3. Шаринг состояния через Context API

Для React компонентов, которые нужно шарить состояние:

// contexts/UserContext.js
import { createContext, useState } from 'react';

export const UserContext = createContext();

export function UserProvider({ children }) {
  const [user, setUser] = useState(null);
  const [isLoading, setIsLoading] = useState(false);
  
  const login = async (email, password) => {
    setIsLoading(true);
    try {
      const response = await fetch('/api/login', {
        method: 'POST',
        body: JSON.stringify({ email, password })
      });
      const data = await response.json();
      setUser(data);
      return data;
    } finally {
      setIsLoading(false);
    }
  };
  
  const logout = () => setUser(null);
  
  return (
    <UserContext.Provider value={{ user, isLoading, login, logout }}>
      {children}
    </UserContext.Provider>
  );
}

// Использование в компонентах
import { useContext } from 'react';
import { UserContext } from './contexts/UserContext';

function Profile() {
  const { user, logout } = useContext(UserContext);
  
  return (
    <div>
      <p>User: {user?.name}</p>
      <button onClick={logout}>Logout</button>
    </div>
  );
}

4. Custom hooks для шаринга логики

Если нужно шарить логику между компонентами:

// hooks/useApi.js
import { useState, useCallback } from 'react';

export function useApi(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  
  const fetch = useCallback(async () => {
    setLoading(true);
    setError(null);
    try {
      const response = await fetch(url);
      if (!response.ok) throw new Error('API Error');
      const result = await response.json();
      setData(result);
    } catch (err) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  }, [url]);
  
  return { data, loading, error, fetch };
}

// Использование
function UsersList() {
  const { data: users, loading, fetch } = useApi('/api/users');
  
  useEffect(() => {
    fetch();
  }, [fetch]);
  
  if (loading) return <p>Loading...</p>;
  
  return users.map(user => <div key={user.id}>{user.name}</div>);
}

5. Singleton паттерн для инициализации один раз

Если нужна одна инстанция класса/сервиса:

// services/AuthService.js
class AuthService {
  constructor() {
    this.token = null;
    this.user = null;
  }
  
  async login(credentials) {
    const response = await fetch('/api/login', {
      method: 'POST',
      body: JSON.stringify(credentials)
    });
    const data = await response.json();
    this.token = data.token;
    this.user = data.user;
    localStorage.setItem('token', this.token);
    return data;
  }
  
  logout() {
    this.token = null;
    this.user = null;
    localStorage.removeItem('token');
  }
  
  getToken() {
    return this.token || localStorage.getItem('token');
  }
}

// Singleton паттерн
let instance = null;

export function getAuthService() {
  if (!instance) {
    instance = new AuthService();
  }
  return instance;
}

// Использование
import { getAuthService } from './services/AuthService';

const auth = getAuthService();
await auth.login({ email: 'user@example.com', password: 'pass' });

// В другом месте кода
const auth2 = getAuthService();
console.log(auth === auth2); // true - одна инстанция
console.log(auth2.getToken()); // уже залогинен

6. Класс-утилиты для константных методов

// utils/formatters.js
export class DateFormatter {
  static toLocaleDateString(date) {
    return new Date(date).toLocaleDateString('ru-RU');
  }
  
  static toLocaleTimeString(date) {
    return new Date(date).toLocaleTimeString('ru-RU');
  }
  
  static formatDuration(ms) {
    const seconds = Math.floor(ms / 1000);
    const minutes = Math.floor(seconds / 60);
    return `${minutes}m ${seconds % 60}s`;
  }
}

export class StringUtils {
  static capitalize(str) {
    return str.charAt(0).toUpperCase() + str.slice(1);
  }
  
  static truncate(str, length) {
    return str.length > length ? str.slice(0, length) + '...' : str;
  }
}

// Использование
import { DateFormatter, StringUtils } from './utils/formatters';

DateFormatter.toLocaleDateString(new Date());  // "02.04.2026"
StringUtils.capitalize('hello');                // "Hello"

7. Шаринг конфигурации и констант

// config/index.js
export const API_BASE_URL = process.env.REACT_APP_API_URL || 'http://localhost:3000';
export const API_TIMEOUT = 30000;
export const RETRY_ATTEMPTS = 3;

export const ROUTES = {
  HOME: '/',
  PROFILE: '/profile',
  SETTINGS: '/settings'
};

export const THEMES = {
  LIGHT: 'light',
  DARK: 'dark'
};

// Использование везде
import { API_BASE_URL, ROUTES } from './config';

const url = `${API_BASE_URL}/api/users`;
navigation.push(ROUTES.PROFILE);

8. Event Emitter для асинхронного шаринга событий

// services/EventEmitter.js
class EventEmitter {
  constructor() {
    this.events = {};
  }
  
  on(event, callback) {
    if (!this.events[event]) {
      this.events[event] = [];
    }
    this.events[event].push(callback);
    
    // unsubscribe функция
    return () => {
      this.events[event] = this.events[event].filter(cb => cb !== callback);
    };
  }
  
  emit(event, data) {
    if (this.events[event]) {
      this.events[event].forEach(callback => callback(data));
    }
  }
}

export const eventBus = new EventEmitter();

// Использование в разных файлах
// file1.js
import { eventBus } from './services/EventEmitter';
eventBus.emit('user:login', { id: 123, name: 'John' });

// file2.js
import { eventBus } from './services/EventEmitter';
eventBus.on('user:login', (user) => {
  console.log('User logged in:', user);
});

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

СпособИспользованиеПлюсыМинусы
ES6 модулиФункции, классы, константыСтандартно, явно, безопасноТребует бандлер
Context APIReact состояниеВстроено в ReactМожет вызвать лишние ре-ренды
Custom hooksReact логикаПереиспользуемо, чистотыТолько для React
SingletonСервисы, утилитыОдна инстанцияМожет быть сложно тестировать
Event EmitterАсинхронные событияСлабая связьСложнее отследить

Правило: избегай window

// ПЛОХО
window.sharedData = { count: 0 };
window.increment = () => window.sharedData.count++;

// ХОРОШО
export const sharedData = { count: 0 };
export const increment = () => sharedData.count++;

// ЕЩЕ ЛУЧШЕ (функциональный подход)
export function createCounter() {
  let count = 0;
  return {
    increment: () => ++count,
    decrement: () => --count,
    get: () => count
  };
}

Этот подход гарантирует:

  • Инкапсуляцию данных
  • Отсутствие глобального загрязнения
  • Простоту тестирования
  • Явную зависимость между модулями
  • Возможность tree shaking в бандлере
Как пошарить код между двумя участками кода без window? | PrepBro