Функции Node.js

Функция — это повторно используемый блок кода, предназначенный для выполнения определённой задачи.

В Node.js функции являются одними из основных компонентов JavaScript и базовыми строительными блоками приложений.

Node.js наследует все особенности функций JavaScript, и они играют важную роль в его асинхронной модели программирования.

В JavaScript функцию можно использовать в качестве аргумента другой функции. Мы можем сначала определить функцию, а затем передать её, или определить функцию непосредственно там, где передаётся аргумент.

Важность функций

  • Повторное использование кода: Избегайте повторного написания одной и той же логики
  • Модульность: Разбивайте сложные задачи на небольшие функции
  • Сопровождаемость: Облегчает отладку и изменение независимых функциональных единиц

Объявление функции

Для объявления функции используйте ключевое словоfunction.

function greet(name) {
    console.log(`Hello, ${name}!`);
}

Функциональное выражение

Присвойте функцию переменной.

const greet = function(name) {
    console.log(`Hello, ${name}!`);
};

Стрелочные функции

Краткие функциональные выражения, введённые в ES6.

const greet = (name) => {
    console.log(`Hello, ${name}!`);
};

// 单行箭头函数
const greet = name => console.log(`Hello, ${name}!`);

Типы функций

1. Обычные функции

Наиболее распространённая форма функции, которая может иметь параметры и возвращаемые значения.

function add(a, b) {
    return a + b;
}

2. Анонимные функции

Функции без имени, обычно передаваемые как аргументы другим функциям.

setTimeout(function() {
    console.log('This is an anonymous function.');
}, 1000);

3. Функции обратного вызова

Передаётся как аргумент другой функции и вызывается после завершения определённой операции.

function fetchData(callback) {
    setTimeout(() => {
        const data = 'Some data';
        callback(data);
    }, 1000);
}

fetchData((data) => {
    console.log(data);
});

Асинхронные функции

От колбэков к промисам.

Пример

function readFilePromise(path) {
  return new Promise((resolve, reject) => {
    fs.readFile(path, 'utf8', (err, data) => {
      if (err) reject(err);
      else resolve(data);
    });
  });
}

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

Пример

async function fetchUser(id) {
    try {
        const response = await fetch(`https://api.example.com/users/${id}`);
        const user = await response.json();
        return user;
    } catch (error) {
        console.error('Error fetching user:', error);
    }
}

fetchUser(1).then(user => console.log(user));

Сравнение методов вызова

Метод вызова Пример Характеристики
Прямой вызов greet('Alice') Самый распространённый способ
Вызов как метод obj.method() this указывает на вызывающий объект
Вызов конструктора new Constructor() Создаёт новый экземпляр
Косвенный вызов greet.call(null, 'Bob') Может изменить привязку this

Продвинутое использование

1. Замыкания

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

Пример

function createCounter() {
    let count = 0;
    return function() {
        count++;
        return count;
    };
}

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

2. Функции высшего порядка

Функции, которые принимают функции в качестве аргументов или возвращают функции.

Пример

function applyOperation(a, b, operation) {
    return operation(a, b);
}

const sum = applyOperation(5, 3, (x, y) => x + y);
const product = applyOperation(5, 3, (x, y) => x * y);

console.log(sum); // 8
console.log(product); // 15

Обработка параметров функций

Параметры по умолчанию

Указывайте значения по умолчанию для параметров во время объявления функции.

function greet(name = 'Guest') {
    console.log(`Hello, ${name}!`);
}

greet(); // Hello, Guest!
greet('Alice'); // Hello, Alice!

Остаточные параметры

Позволяет представить неограниченное количество аргументов в виде массива.

Пример

function sum(...numbers) {
    return numbers.reduce((acc, num) => acc + num, 0);
}

console.log(sum(1, 2, 3, 4)); // 10

Деструктурированные параметры

Извлекайте данные из объектов или массивов и присваивайте их переменным.

Пример

function getUserInfo({ name, age }) {
    console.log(`Имя: ${name}, Возраст: ${age}`);
}

const user = { name: 'Alice', age: 30 };
getUserInfo(user); // Имя: Alice, Возраст: 30

Пример

Единственная ответственность функций:Каждая функция должна делать только одну вещь, что упрощает тестирование и поддержку.

Пример

function calculateArea(width, height) {
    return width * height;
}

Избегайте глобальных переменных:Сведите использование глобальных переменных к минимуму; вместо этого используйте локальные переменные и параметры функций.

Пример

function calculateTotal(items) {
    let total = 0;
    for (const item of items) {
        total += item.price * item.quantity;
    }
    return total;
}

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

Пример

const users = [
    { id: 1, name: 'Alice' },
    { id: 2, name: 'Bob' }
];

const names = users.map(user => user.name);
console.log(names); // ['Alice', 'Bob']

Обработка ошибок:Используйте операторы try...catch для обработки ошибок, которые могут быть выброшены.

Пример

async function fetchUser(id) {
    try {
        const response = await fetch(`https://api.example.com/users/${id}`);
        if (!response.ok) {
            throw new Error('Network response was not ok');
        }
        const user = await response.json();
        return user;
    } catch (error) {
        console.error('Error fetching user:', error);
    }
}

Функции как параметры:Используйте функцию в качестве аргумента.

Пример

function say(word) {
  console.log(word);
}

function execute(someFunction, value) {
  someFunction(value);
}

execute(say, "Hello");

В приведённом выше коде мы передаём функцию say в качестве первого аргумента функции execute. Здесь передаётся не возвращаемое значение say, а сама say!

В результате say становится локальной переменной someFunction внутри execute, и execute может использовать функцию say, вызвав someFunction() (с круглыми скобками).

Конечно, поскольку у say есть параметр, execute может передать такой аргумент при вызове someFunction.

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

Пример

function execute(someFunction, value) {
  someFunction(value);
}

execute(function(word){ console.log(word) }, "Hello");

Мы определяем функцию, которую собираемся передать в execute, прямо там, где execute принимает свой первый аргумент.

Таким образом, нам даже не нужно давать функции имя, поэтому она называется анонимной функцией.


Как передача функций заставляет HTTP-сервер работать

С этими знаниями давайте ещё раз взглянем на наш простой, но не такой уж и простой HTTP-сервер:

Пример

var http = require("http");

http.createServer(function(request, response) {
  response.writeHead(200, {"Content-Type": "text/plain"});
  response.write("Hello World");
  response.end();
}).listen(8888);

Теперь это должно выглядеть гораздо понятнее: мы передаём анонимную функцию в функцию createServer.

Той же цели можно достичь с помощью такого кода:

Пример

var http = require("http");

function onRequest(request, response) {
  response.writeHead(200, {"Content-Type": "text/plain"});
  response.write("Hello World");
  response.end();
}

http.createServer(onRequest).listen(8888);

Лучшие практики работы с функциями

Принципы проектирования функций

  1. Единственная ответственность: функция выполняет только одну задачу
  2. Разумное именование: используйте форму глагол + существительное (например,getUserInfo)
  3. Контроль длины: рекомендуется не превышать 20 строк кода
  4. Избегайте побочных эффектов: чистые функции проще тестировать и поддерживать

Советы по оптимизации производительности

Пример

// Используйте замыкание для кэширования результатов
function memoize(fn) {
  const cache = new Map();
  return (...args) => {
    const key = JSON.stringify(args);
    if (cache.has(key)) return cache.get(key);
    const result = fn(...args);
    cache.set(key, result);
    return result;
  };
}

Практическое занятие

Упражнение 1: Создайте функцию преобразования температуры

Пример

/**
* Преобразование между градусами Цельсия и Фаренгейта
* @param {number} temp - Значение температуры
* @param {string} unit - Исходная единица ('C' или 'F')
* @returns {number} Преобразованная температура
 */

function convertTemperature(temp, unit) {
  // Ваш код...
}

Упражнение 2: Реализуйте простой эмиттер событий

Пример

class EventEmitter {
  constructor() {
    this.events = {};
  }
 
  // Реализуйте методы on/emit/off
}
Другие расширения