Считайте это продолжением моей статьи от 2017 года: Настройка вашей первой среды Node.js! Это своего рода руководство, которое я хотел бы иметь, когда впервые перешел на работу с node.js в 2017 году, пришедший из мира PHP до этого.

Если вы веб-разработчик, работающий с другими серверными языками, такими как PHP и стандартный интерфейс JS (или jQuery), скорее всего, вы уже знакомы с парадигмами кодирования на стороне сервера, а также с обычным синтаксисом Javascript. Хорошая новость заключается в том, что это на самом деле дает вам прочную основу для начала работы с node.js, поскольку весь node.js просто пишет программы на стороне сервера с использованием синтаксиса Javascript.

ES6 (ES2015) и версия узла 8+

Теперь вы могли бы использовать старый школьный ванильный Javascript для написания всего кода вашего сервера node.js. Но вы бы: 1) упустили многое из того, что делает разработку в node.js простой и удобной (и даже в некоторых случаях производительной), 2) испытываете проблемы с пониманием фрагментов кода node.js, которые вы найдете в Интернете, которые все использует.

Я часто говорю, что если вы уже знакомы с ванильным JS, вам действительно нужно выучить еще несколько концепций, чтобы эффективно кодировать на ES6 и в узлах версии 8+. Существуют более новые версии ES (ECMAScript), которые используются в более новых версиях узлов (в ближайшее время - узел 10, узел 12 и другие), но большинство кодовых баз node.js, которые вы найдете, чаще всего используют функции до ES6; поэтому как минимум лучше всего освоить его.

Вот несколько функций ES6, которые вы захотите использовать - для каждой из них краткое изложение и подробный раздел.

1. let и const вместо var

tl; dr: let и const - новые ключевые слова для объявления переменной вместо var.

// Old JS (still works, but almost no one uses this anymore)
var foo_1 = 'bar';
// The ES6 / node 8+ way
let foo_2 = 'bar';
foo_2 = 'bar2'; // re-assign its value
// Consts values can't change
const foo_3 = 'bar';
foo_3 = 'bar2'; // this gives an error

Подробности: let и var имеют различия в области охвата; а const нельзя переназначить.

Короче говоря, let имеет меньшую / более точную область действия, что объясняется в главном ответе на Stackoverflow, поэтому его обычно безопаснее использовать. Если вы хотите действительно продвинуться и учиться действительно глубоко, использование let предотвращает попадание в ловушку временной мертвой зоны, что вы можете сделать с var.

У const есть и другие предостережения. Во-первых, в отличие от таких языков, как C ++, где константы обычно представляют собой просто литералы (строки или числа), эти const могут быть объектами и массивами, могут быть результатом функции и обычно могут быть любыми переменными, за исключением того, что вы не можете переназначить его. Обратите внимание, что вы * можете * изменять элементы объекта, который является const. Примеры:

const foo_1 = getFoo(1); // This is fine
const foo_2 = { hello: 'world' }; // This is fine
foo_2.hello = 'earth'; // This is fine too!

В кодовой базе ES6 node.js вы обычно обнаружите, что многие переменные, которые вы обычно ожидаете объявить как переменные в аналогичном коде на других языках, объявлены как consts.

2. Шаблонные литералы / шаблонные строки

tl; dr: более простой способ комбинировать некоторые строки с переменными вместо использования знака плюса + для конкатенации.

const str = 'world';
console.log(`Hello ${str}!`); // prints Hello world!
// The old way would have been...
console.log('Hello ' + str + '!');
// You can put entire expressions within them like a function call
// just like you would anywhere else:
console.log(`Hello ${getStr()}!`);

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

3. require и module.exports

tl; dr: загружает другие файлы кода.

// File one: getHello.js
module.exports = function(param) {
  return `Hello ${param}!`;
}
// File two: index.js
const getHello = require('./getHello');
console.log(getHello('world')); // prints Hello world!

Подробности. Необходимо помнить об ограничении объема, но обычно вы можете рассматривать его как require в php или как импорт на других языках (Java, python).

Что касается области видимости, наиболее важно понимать, что в файле, если что-то не установлено как module.exports, никакие переменные, объекты или функции внутри него не будут доступны любому коду, который пытается require это сделать.

Вы можете установить что угодно как module.exports файла. Обычно это функция или объект (функций), но это также может быть массив, строка или что угодно.

4. Стрелочные функции (жирные стрелки)

tl; dr: более короткий способ определения функций (особенно анонимных), который сейчас используют все:

// Defining a function and giving it a name
const myFunc = (param1, param2) => {
  return 'Hello!';
};
// If there is only one param, even the parenthesis is optional!
// This can look confusing at first. It works the same way.
const myFunc2 = param1 => {
  return 'Hello!';
};
// Passing in a function as a parameter, such as a callback.
// Note the lack of parameters here, which is valid.
setTimeout(() => { console.log('Hi'); }, 1000);

Подробности: разница в привязке. Одним из основных отличий использования стрелочной функции является то, что объект this ведет себя по-другому (см. Здесь). Но есть вероятность, что функциональная кодовая база JS не будет очень много использовать this.

Один изящный трюк, который может быть предостережением, если вы не знаком с ним, заключается в том, что стрелочные функции позволяют вам возвращать однострочник напрямую, если вы опускаете фигурные скобки:

const getHello = param => `Hello ${param}!`; // Valid function!
console.log(getHello('world')); // prints Hello world!

Подробности смотрите здесь!

5. Деструктуризация объектов и массивов

tl; dr: более короткий / удобный способ «разбить» элементы объекта (или массива) на отдельные переменные.

// Example of an object. This can be returned from a function, etc.
const obj = {
  one: 1,
  two: 2,
  three: 3,
  four: 4,
  five: 5
};
// You just need one and four? Sure!
const { one, four } = obj;
// Now the consts "one" and "four" are ready to be used.
// Alternatively, name them something else as you declare them:
const { one: foo, four: bar } = obj;
// Now foo = 1, and bar = 4!
// Similar things can be done with arrays
const arr = [1, 2, 3, 4, 5];
const [one, two] = arr; // one = 1, two = 2

Деталь. Больше ничего особенного! Единственное, что следует отметить, это то, что он часто используется в качестве параметра для вызовов функций. Если вы не знакомы с этим и не замечали этого, это может сбивать с толку:

function doStuff({one, two, three}) {
  console.log(`Hey ${one} ho ${two} and ${three}!`);
}
// This allows you to pass in arguments in a more flexible manner,
// and also be more clear what you're passing in:
doStuff({three: 'foo', two: 'bar'});

6. Расширенные объектные литералы

tl; dr: сокращенный способ создания объекта с ключевыми именами, для которых у вас уже есть переменные.

const one = 1;
const two = 2;
const three = 3;
const myObj = { one, two, three };
// This is equivalent to:
// { one: one, two: two, three: three }

Подробности. Не более того! На самом деле это всего лишь короткая рука / удобная функция. Другой аспект расширенных объектных литералов - наличие вычисляемых ключей свойств:

const f = 'foo';
const myObj = {
  [f + 'bar']: true
};
// myObj is now { foobar: true }

Подробности смотрите здесь!

7. Обещания

tl; dr: альтернатива функциям обратного вызова, которая улучшает внешний вид кода.

// consider doing two http requests sequentially in callbacks...
const request = require('request');
request('https://www.google.com', function (err, resp, body) {
  request('https://www.yahoo.com', function (err, resp, body) {
    // do stuff with the result
  });
});
// similar code but in promise...
const requestpromise = require('request-promise'); // different lib
requestpromise('https://www.google.com')
  .then(function(body) {
    return requestpromise('https://www.yahoo.com');
  })
  .then(function(body) {
    // do stuff with the result
  })
  .catch(function(err) {
    // error handling
  });

Подробности. Проблема с обратными вызовами заключается в том, что, как только вы начинаете выполнять несколько этапов асинхронных операций по порядку (например, несколько HTTP-запросов), вы начинаете попадать в «ад обратных вызовов», где вы оборачиваете еще и больше обратных вызовов. Обещания решают эту проблему.

Обещания также предоставляют множество других преимуществ, в первую очередь использование асинхронной природы node.js и одновременное выполнение нескольких вызовов / запросов, ожидающих ввода-вывода:

const rp = require('request-promise');
Promise.all([
  rp('https://www.google.com'),
  rp('https://www.yahoo.com')
])
  .then(function([body1, body2]) {
    // do stuff with both response bodies
  })
  .catch(function(err) {
    // this catches any error with either of the calls
  });

8. Асинхронный / ожидающий

tl; dr. Основываясь на обещаниях, позвольте вам написать код, который использует обещания процедурным образом.

const rp = require('request-promise');
// Anything that returns a Promise can be await'd.
const body1 = await rp('https://www.google.com');
const body2 = await rp('https://www.yahoo.com');
// do stuff with body1 and body2
// You can still use Promise.all() to run them in parallel:
const [body1, body2] = await Promise.all([
  rp('https://www.google.com'),
  rp('https://www.yahoo.com')
]);
// Functions that contain "await" in its body need to be declared
// as "async" explicitly.
async function doStuff() {
  const body = await rp('https://www.google.com');
  // do stuff
}
// async functions are automatically implied to return a promise,
// so calling async functions will need an await:
await doStuff();
// Since that's just a regular promise, you can run in parallel too:
await Promise.all([
  doStuff(),
  rp('https://www.yahoo.com')
]);
// Error handling looks like this:
try {
  await doStuff();
} catch (err) {
  // error handling here
}

Подробности: я привел довольно много примеров в образце кода для этого, так что они почти полностью покрывают его.

Самое интересное в том, что когда вы приехали из такого мира, как PHP, шаблон await кажется почти более естественным, чем исходные механизмы обратного вызова и обещания в node.js, потому что он делает код более синхронным и процедурным.

После того, как вы усвоили вышеуказанные концепции, вы уже прошли большую часть пути к разработке на node.js.

Следующим шагом будет изучение функционального программирования и того, как выглядит структура кода node.js, основанная на функциональности, а это нечто более высокое, чем возможности языка. Функционально-ориентированное программирование - это парадигма, отличная от объектно-ориентированного программирования; углубление в это выходит за рамки данной статьи. Может быть в следующий раз? ;)