[Перевод] 7 продвинутых приёмов JavaScript, которые должен знать каждый разработчик

367e924047a2bfc9504b390ae2e93d64.png

JavaScript продолжает развиваться, давая разработчикам всё больше мощных инструментов для написания чистого, быстрого и эффективного кода.

Однако при таком обилии функций и технологий легко упустить самые мощные из них. Если ваша цель — повысить производительность или упростить поддержку кода, эти продвинутые техники дадут вам серьёзное преимущество.

Давайте разберём 7 продвинутых техник в JavaScript, которые помогут вам вывести свои навыки на новый уровень.

1. Замыкания сделают код чище

Замыкания — это одна из самых мощных и при этом часто неправильно понимаемых фишек в JavaScript. С их помощью можно создавать функции с приватными переменными, что делает код более структурированным и безопасным.

Что такое замыкание? Это когда функция «помнит» свою область видимости, даже после того, как завершила выполнение. Такой подход полезен, если нужно сохранять состояние внутри функции, не прибегая к глобальным переменным.

// Example of a closure
function createCounter() {
  let count = 0;
  return function() {
    count++;
    return count;
  };
}


const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2

Примеры использования: замыкания идеальны для сохранения состояния в обработчиках событий, создания приватных переменных или написания функций высшего порядка.

2. Деструктуризация сделает код проще

Деструктуризация — это удобная фишка ES6, позволяющая извлекать значения из массивов или объектов и присваивать их переменным более кратко и понятно. Это упрощает код и делает его легче для чтения и поддержки.

// Object destructuring
const person = { name: 'Alice', age: 30 };
const { name, age } = person;


console.log(name); // 'Alice'
console.log(age);  // 30


// Array destructuring
const numbers = [1, 2, 3];
const [first, second] = numbers;


console.log(first);  // 1
console.log(second); // 2

Примеры использования: деструктуризация особенно полезна при работе с API или сложными объектами, когда нужно забрать только нужные данные.

3. Debouncing и Throttling — как оптимизировать производительность

При обработке пользовательских событий, таких как прокрутка или изменение размера окна, частое срабатывание функций на каждое действие пользователя может сильно замедлить работу приложения. Debounce и Throttling — два метода, которые помогают регулировать частоту выполнения функций.

Debouncing  — это когда функция срабатывает только после того, как действие пользователя завершилось и прошло определённое время без новых событий.

Throttling — функция выполняется не чаще, чем один раз за установленный промежуток времени.

// Debounce function
function debounce(func, delay) {
  let timeout;
  return function(...args) {
    clearTimeout(timeout);
    timeout = setTimeout(() => func.apply(this, args), delay);
  };
}


// Throttle function
function throttle(func, limit) {
  let inThrottle;
  return function(...args) {
    if (!inThrottle) {
      func.apply(this, args);
      inThrottle = true;
      setTimeout(() => inThrottle = false, limit);
    }
  };
}

Эти техники отлично подходят для оптимизации производительности при вводе текста в поисковую строку, обработке событий прокрутки или изменения размера окна.

4. Каррирование повышает гибкость функций

Каррирование — это техника, которая превращает функцию с несколькими аргументами в цепочку функций, каждая из которых принимает по одному аргументу. 

Это делает функции более гибкими и позволяет применять их частично.

// Basic curry function
function curry(fn) {
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn.apply(this, args);
    } else {
      return function(...nextArgs) {
        return curried.apply(this, args.concat(nextArgs));
      };
    }
  };
}


// Usage
const add = (a, b, c) => a + b + c;
const curriedAdd = curry(add);


console.log(curriedAdd(1)(2)(3)); // 6

Каррирование особенно полезно при создании сложных функций, которые можно использовать с частично готовыми данными, например, в функциональном программировании или в компонентах React.

5. Proxy перехватывает действия объектов

Proxy позволяет перехватывать и изменять поведение объектов, такие как доступ к свойствам, их изменение или вызов функций. Это удобно для проверки данных, логирования или создания реактивных систем.

const person = {
  name: 'John',
  age: 25
};


const handler = {
  get: function(target, property) {
    console.log(`Getting property ${property}`);
    return property in target ? target[property] : 'Property not found';
  },
  set: function(target, property, value) {
    if (property === 'age' && value < 0) {
      console.error('Age cannot be negative');
    } else {
      target[property] = value;
    }
  }
};


const proxyPerson = new Proxy(person, handler);
console.log(proxyPerson.name); // Logs "Getting property name" and outputs "John"
proxyPerson.age = -5; // Logs "Age cannot be negative"

Proxy часто используется для валидации данных, в таких реактивных фреймворках, как Vue.js, а также для отслеживания доступа к конфиденциальной информации.

6. Как работает цикл событий и асинхронный JavaScript

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

Понимание цикла событий необходимо для написания эффективного асинхронного кода, особенно если вы работаете с setTimeout, Promises или async/await.

console.log('Start');


setTimeout(() => {
  console.log('Inside setTimeout');
}, 0);


Promise.resolve().then(() => {
  console.log('Inside Promise');
});


console.log('End');
// Output: 
// Start
// End
// Inside Promise
// Inside setTimeout

Знание цикла событий критически важно при создании реальных приложений, обработке API-запросов и управлении асинхронными задачами.

7. Мемоизация для улучшения производительности

Мемоизация — это способ кэшировать результаты ресурсоемких функций и возвращать их из кэша при повторных вызовах с одинаковыми аргументами. Это существенно ускоряет функции, которые часто вызываются с одинаковыми входными данными.

function memoize(fn) {
  const cache = new Map();
  return function(...args) {
    const key = JSON.stringify(args);
    if (cache.has(key)) {
      return cache.get(key);
    }
    const result = fn.apply(this, args);
    cache.set(key, result);
    return result;
  };
}


// Usage
const slowFunction = (num) => {
  console.log('Long computation...');
  return num * 2;
};


const memoizedFunction = memoize(slowFunction);
console.log(memoizedFunction(5)); // Long computation... 10
console.log(memoizedFunction(5)); // 10 (from cache)

Мемоизация особенно полезна для оптимизации сложных вычислений в приложениях, работающих с большими объемами данных, например, при сортировке массивов или выполнении математических операций.

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

© Habrahabr.ru