❓👨‍💻 Вопросы для подготовки к собеседованию по JavaScript. Часть 1

Стрелочные функции, замыкания, промисы и async/await, методы работы с массивами и объектами, шаблонные литералы, особенности Map и Set, оператор расширения и клонирование объектов.

1. Какие типы данных есть в JavaScript?

Какие типы данных есть в JavaScript?

Number – число. Тип Number в JavaScript может хранить как целые числа, так и числа с плавающей точкой.

BigInt – используется для представления целых чисел произвольной длины, превышающих 2^53 – 1. BigInt создается с помощью добавления n в конец целочисленного литерала или путем вызова функции BigInt(), которая создает BigInt из строк, чисел и т.д.:

// Создание BigInt путем добавления n в конец целочисленного литерала
const bigInt1 = 1234567890123456789012345678901234567890n;

// Создание BigInt путем вызова функции BigInt()
const bigInt2 = BigInt("1234567890123456789012345678901234567890");

// Создание BigInt из числа
const bigInt3 = BigInt(10); // Это то же самое, что и 10n

// Сложение
console.log(1n + 2n); // 3n

// Деление
console.log(5n / 2n); // 2n

String – строка (последовательность символов), например, "JavaScript – главный язык интернета".Строки записываются с использованием кавычек, можно использовать одинарные или двойные кавычки.

Boolean – логический (булев) тип, который может принимать значения true (истина) или false (ложь).

Object – объект. Это значение в памяти, на которое возможно сослаться с помощью идентификатора. Объект может расцениваться как набор свойств. Значения свойств могут иметь любой тип, включая другие объекты, что позволяет строить сложные, разветвленные иерархии данных.

null – специальное значение, которое представляет «ничего», «пусто», или «неизвестное значение».

undefined – это значение присваивается переменной, если она была объявлена, но не получила значения.

Symbol – это уникальный и неизменяемый тип данных, который можно использовать в качестве идентификатора для свойств объекта.

Больше полезных материалов вы найдете на нашем телеграм-канале «Библиотека фронтендера»

2. В чем состоит различие между == и ===?

В чем состоит различие между == и ===?

В JavaScript операторы == и === используются для сравнения двух значений, но они работают по-разному:

== проверяет на абстрактное равенство, то есть он преобразует типы данных перед сравнением – например, если вы сравниваете строку с числом, JavaScript преобразует строку в число перед сравнением. Если строка не может быть преобразована в число, она преобразуется в NaN, что возвращает false. Если оба операнда имеют разные типы данных, но они могут быть преобразованы в один и тот же тип данных и имеют одно и то же значение, оператор == вернет true:

  var a = 10;
  console.log(a == 20); // false
  console.log(a == "1o"); // false
  console.log(a == "10"); // true

=== проверяет на строгое равенство, то есть он не выполняет преобразование типов данных. Если два значения имеют разные типы данных, оператор === вернет false, даже если они имеют одно и то же значение. Если оба операнда имеют одинаковый тип данных и одинаковое значение, оператор === вернет true:

  var x = 10;
  console.log(x === "10"); // false
  console.log(x === 10); // true
Вакансии по фронтенду
Вакансии по фронтенду, джаваскрипт, React, Angular, Vue @jsdevjob

3. Какие способы объявления переменных есть в JavaScript?

В JavaScript есть четыре способа объявления переменных:

myVariable = 5;
var myVariable = 5;
let myVariable = 5;
const myVariable = 5;

myVariable = 5; – неявное объявление переменной. Оно создает глобальную переменную myVariable и присваивает ей значение 5, что может привести к ошибкам в строгом режиме:

"use strict";
myVariable = 5; // ReferenceError: myVariable is not defined

var myVariable = 5; явное объявление переменной с использованием ключевого слова var. Область видимости переменной myVariable может быть функциональной или глобальной, если она объявлена вне функции. Недостаток var состоит в том, что ее область видимости не ограничивается блоком, в котором используется переменная:

function example() {
 var myVariable = 1;
 if (true) {
   var myVariable = 2; // Переопределяет myVariable из внешнего блока
   console.log(myVariable); // Выводит 2
 }
 console.log(myVariable); // Выводит 2
}
example();

let myVariable = 5; – явное объявление переменной с использованием ключевого слова let. Область видимости такой переменной ограничивается блоком, в котором она объявлена – на уровне функции она не видна:

function example() {
 let myVariable = 5;
 if (true) {
   let myVariable = 25; // Это новая переменная myVariable, которая видна только внутри этого блока
   console.log(myVariable); // Выводит 25
 }
 console.log(myVariable); // Выводит 5
}
example();

const myVariable = 5; – это явное объявление переменной с использованием ключевого слова const. Переменная myVariable объявляется в области видимости блока, как и let. Однако, в отличие от let, переменная myVariable является неизменяемой – ее значение не может быть изменено после объявления, за одним исключением: если значение является объектом или массивом, его свойства или элементы могут быть изменены, но сама переменная все равно остается неизменяемой:

const myVariable = 5;
console.log(myVariable); // Выводит 5
myVariable = 10; // TypeError: Assignment to constant variable.
const myObject = { key: "value" };
myObject.key = "otherValue"; // Это допустимо
console.log(myObject.key); // Выводит "otherValue"

const myArray = [];
myArray.push("A"); // Это допустимо
console.log(myArray); // Выводит ["A"]

4. В чем разница между null и undefined?

В чем разница между null и undefined?

В JavaScript и null, и undefined представляют отсутствие значения, но они используются в разных контекстах и имеют разные семантические значения.

undefined – присваивается переменной, когда она объявлена, но ей не присвоено конкретное значение:

var testVar;
console.log(testVar); // Выводит undefined
console.log(typeof testVar); // Выводит undefined

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

var testVar = 5;
console.log(testVar); // Выводит 5
var testVar = null;
console.log(testVar); // Выводит null
console.log(typeof testVar); // Выводит object

5. Чем стрелочные функции отличаются от обычных?

Чем стрелочные функции отличаются от обычных?

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

Стрелочные функции не могут использовать объект arguments. В обычных функциях этот объект содержит все переданные при вызове аргументы:

function sum() {
  console.log(arguments); // { '0': 1, '1': 2, '2': 3 }
}

sum(1, 2, 3);

const sumArrow = () => {
  console.log(arguments); // ReferenceError: arguments is not defined
}

sumArrow(1, 2, 3);

У стрелочных функций другой синтаксис записи. Они записываются короче, используя стрелку => и не требуя ключевого слова function:

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

const sumArrow = (a, b) => a + b;

У стрелочных функций нет собственного контекста this. Вместо этого контекст берется из внешней области видимости:

const myObject = {
 text: "JavaScript - очень простой язык",
 description: function() {
   setTimeout(() => {
     console.log(this.text);
   }, 1000);
 }
};

myObject.description(); // Выводит "JavaScript - очень простой язык"

Стрелочные функции нельзя использовать как конструкторы с ключевым словом new. То есть, из них нельзя создавать объекты при помощи оператора new:

const myFunction = () => {};
const newFunction = new myFunction(); // TypeError: myFunction is not a constructor

6. Что такое замыкание?

Замыкание (closure) в JavaScript – это комбинация функции и лексического окружения, в котором эта функция была определена. Такая функция имеет доступ к переменным внешней функции, даже после того, как внешняя функция завершила выполнение:

function createAdder(x) {
 return function(y) {
   return x + y;
 };
}

const add5 = createAdder(5);
const add10 = createAdder(10);

console.log(add5(2)); // Выводит 7
console.log(add10(2)); // Выводит 12

7. Что такое шаблонные строки (литералы)?

Шаблонные строки (template literals) в JavaScript – это новый способ работы со строками, введенный в ECMAScript 6 (ES6). Они обозначаются обратными кавычками ` вместо одинарных или двойных кавычек. Шаблонные строки позволяют создавать многострочные строки без необходимости использования специальных символов или конкатенации строк:

let multilineString = `Это строка номер 1
Это строка номер 2
Это строка номер 3`;
console.log(multilineString);

Шаблонные строки также поддерживают интерполяцию строк, что позволяет вставлять выражения прямо в строку. Эти выражения заключаются в фигурные скобки ${expression} и вычисляются при создании строки:

let name = "Василий Пупкин";
let dailyBonus = 0;
let greeting = `Привет, ${name}! Тебе начислен ежедневный бонус - ${dailyBonus + 5} руб`;
console.log(greeting); // Выводит: Привет, Василий Пупкин! Тебе начислен ежедневный бонус - 5 руб.

8. Что такое Map и Set в JavaScript?

Map и Set – это два типа коллекций, которые были введены в ECMAScript 6 (ES6). Они предоставляют более гибкие и мощные способы работы с наборами данных по сравнению с обычными объектами и массивами.

Map – это коллекция, которая состоит из пар ключ-значение, подобно объектам. Основное отличие Map от объектов заключается в том, что Map запоминает порядок добавления пар и позволяет использовать в качестве ключей данные любых типов:

let user1 = { name: 'Егор' };
let user2 = { name: 'Марина' };

let userRoles = new Map();
userRoles.set(user1, 'администратор');
userRoles.set(user2, 'редактор');

console.log(userRoles.get(user1)); // Выводит: администратор
console.log(userRoles.get(user2)); // Выводит: редактор

При желании в Map в качестве ключей можно использовать функции:

function sayHello() {
 console.log('приветствие');
}

function sayGoodbye() {
 console.log('прощание');
}

let functionMap = new Map();
functionMap.set(sayHello, 'Привет!');
functionMap.set(sayGoodbye, 'До свидания!');

console.log(functionMap.get(sayHello)); // Выводит: Привет!
console.log(functionMap.get(sayGoodbye)); // Выводит: До свидания!

Set – это множество, в котором каждое значение может появляться только один раз. Дубликатов в Set нет:

const mySet = new Set();
mySet.add('яблоко');
mySet.add('апельсин');
mySet.add('апельсин'); // Это не добавит 'апельсин' второй раз, так как такое значение уже есть
console.log(mySet.has('яблоко')); // Выводит: true
console.log(mySet.has('апельсин')); // Выводит: true
console.log(mySet.has('банан')); // Выводит: false

9. Как проверить наличие свойства в объекте?

Как проверить наличие свойства в объекте?

В JavaScript есть два основных способа проверить наличие свойства в объекте – метод hasOwnProperty и оператор in.

Метод hasOwnProperty() возвращает true, если указанное свойство является прямым свойством объекта, и false в противном случае. Этот метод не проверяет свойства в цепочке прототипов объекта. Оператор in возвращает true, если указанное свойство существует в объекте, независимо от того, является ли оно собственным свойством или унаследовано:

const book = {
 title: "Великий Гэтсби",
 author: "Ф. Скотт Фицджеральд",
 year: 1925
};

console.log(book.hasOwnProperty("title")); // true
console.log("title" in book); // true
console.log("year" in book); // true
console.log("language" in book); // false

10. Как получить доступ к свойствам объекта?

В JavaScript есть два основных способа доступа к свойствам объекта: статический (с использованием точечной нотации) и динамический (с использованием квадратных скобок).

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

const movie = {
 title: "Крестный отец",
 director: "Фрэнсис Форд Коппола",
 year: 1972
};

console.log(movie['title']); // Крестный отец
console.log(movie.director); // Фрэнсис Форд Коппола

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

var propertyName = "title";
var book = {
 title: "Анна Каренина",
 author: "Лев Толстой",
 year: 1877
};

console.log(book[propertyName]); // Анна Каренина

11. Какие основные методы работы с массивами есть в JavaScript?

Какие основные методы работы с массивами есть в JavaScript?

Oсновные методы для работы с массивами – forEach, filter, map и reduce.

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

const array = ['один', 'два', 'три'];
array.forEach(element => console.log(element));
/* 
 вывод:
 один
 два
 три
*/

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

const numbers = [1, 2, 3, 4, 5];
const evenNumbers = numbers.filter(number => number % 2 === 0);
console.log(evenNumbers); // [2, 4]

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

const numbers = [1, 2, 3, 4, 5];
const squares = numbers.map(number => number * number);
console.log(squares); // [1, 4, 9, 16, 25]

Метод reduce выполняет функцию для каждого элемента массива, накапливая результат в одном значении. Используется, когда нужно объединить все элементы массива в одно значение, например, вычислить сумму всех чисел в массиве:

const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
console.log(sum); // 15

12. Какие способы создания объектов есть в JavaScript?

Объекты JavaScript создаются с помощью функции-конструктора, литеральной нотации объекта, класса и метода Object.create().

Функция-конструктор – это специальная функция, которую можно использовать для создания объектов с определенными свойствами и методами. Функция-конструктор используется с ключевым словом new:

function Movie(title, director, year) {
 this.title = title;
 this.director = director;
 this.year = year;
}

const movie = new Movie('Дракула Брэма Стокера', 'Фрэнсис Форд Коппола', 1992);
console.log(movie); // { title: 'Дракула Брэма Стокера', director: 'Фрэнсис Форд Коппола', year: 1992 }

Литеральная нотация объекта позволяет создать объект, указав его свойства и значения внутри фигурных скобок {}:

const movie = {
 title: 'Сердце Ангела',
 director: 'Алан Паркер',
 year: 1987
};

console.log(movie); // { title: 'Сердце Ангела', director: 'Алан Паркер', year: 1987 }

Классы позволяют создавать объекты с помощью синтаксиса, похожего на классы в других языках программирования:

class Movie {
 constructor(title, director, year) {
   this.title = title;
   this.director = director;
   this.year = year;
 }
}

const movie = new Movie('Шоссе в никуда', 'Дэвид Линч', 1997);
console.log(movie); // { title: 'Шоссе в никуда', director: 'Дэвид Линч', year: 1997 }

Метод Object.create() позволяет создать новый объект, используя существующий объект в качестве прототипа для нового объекта. Этот метод принимает два аргумента: прототип и объект свойств. Объект свойств определяет свойства нового объекта и их атрибуты configurable, enumerable, writable и value:

const moviePrototype = {
 title: 'Интерстеллар',
 director: 'Кристoфер Нолан',
 year: 2014
};

const movie = Object.create(moviePrototype, {
 title: {
  value: 'Интерстеллар',
  writable: true,
  enumerable: true,
  configurable: true
 },
 director: {
  value: 'Кристoфер Нолан',
  writable: true,
  enumerable: true,
  configurable: true
 },
 year: {
  value: 2014,
  writable: true,
  enumerable: true,
  configurable: true
 }
});

console.log(movie); // { title: 'Интерстеллар', director: 'Кристoфер Нолан', year: 2014 }

13. Что такое Promise (промис)?

Что такое Promise (промис)?

Промис (Promise) — специальный объект JavaScript, который используется для написания и обработки асинхронного кода. Он имеет три состояния:

  • pending – начальное состояние, означает, что асинхронная операция еще не завершена.
  • fulfilled – операция успешно завершена.
  • rejected – операция завершена с ошибкой.

Промисы создаются с помощью конструктора new Promise(). Этот конструктор принимает в качестве аргумента функцию, которая выполняет асинхронную операцию. Функция принимает два аргумента resolve и reject, которые используются для изменения состояния промиса. Если асинхронная операция завершена успешно, вызывается resolve, если произошла ошибка, вызывается reject:

let promise = new Promise((resolve, reject) => {
 // асинхронная операция
 setTimeout(() => {
   resolve("Результат асинхронной операции");
 }, 1000);
});

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

let promise = new Promise((resolve, reject) => {
 // асинхронная операция
 setTimeout(() => {
   resolve("Результат асинхронной операции");
 }, 1000);
});

promise
 .then(result => {
   console.log("Результат: " + result);
 })
 .catch(error => {
   console.log("Ошибка: " + error);
 });

Промисы можно связывать в цепочки, что позволяет выполнять несколько асинхронных операций последовательно. Для этого результат каждого промиса передается в следующий промис в цепочке. Это делается с помощью метода .then():

let promise1 = new Promise((resolve, reject) => {
 setTimeout(() => {
   resolve("Результат первой операции");
 }, 1000);
});

let promise2 = new Promise((resolve, reject) => {
 setTimeout(() => {
   resolve("Результат второй операции");
 }, 2000);
});

promise1
 .then(result1 => {
   console.log("Результат первой операции: " + result1);
   return promise2;
 })
 .then(result2 => {
   console.log("Результат второй операции: " + result2);
 });

14. Что такое async/await и как они используются?

Что такое async/await и как они используются?

Async/await – это синтаксис JavaScript, который облегчает работу с промисами. Ключевое слово async перед функцией означает, что функция всегда возвращает промис. Ключевое слово await используется внутри асинхронных функций и заставляет JavaScript ожидать, пока промис не будет выполнен, прежде чем продолжить выполнение кода:

async function example() {
 let promise = new Promise((resolve, reject) => {
   setTimeout(() => resolve("Сделано!"), 2000)
 });
 let result = await promise; // жди выполнения промиса
 alert(result); // Сделано! появляется в подтверждении действия
}
example();

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

async function fetchDataFromApi() {
 const res = await fetch('https://v2.jokeapi.dev/joke/Programming?type=single');
 const json = await res.json();
 console.log(json.joke);
}
await fetchDataFromApi(); // SyntaxError: await is only valid in async functions

Чтобы решить эту проблему, можно обернуть вызов в другую асинхронную функцию:

async function fetchDataFromApi() {
 const res = await fetch('https://v2.jokeapi.dev/joke/Programming?type=single');
 const json = await res.json();
 console.log(json.joke);
}
async function init() {
 await fetchDataFromApi();
 console.log('Принес новый анекдот!');
}
init();

15. Как проверить, является ли объект массивом?

Для такой проверки можно использовать встроенный метод Array.isArray(). Этот метод принимает объект в качестве аргумента и возвращает true, если объект является массивом, и false в противном случае:

let arr = [1, 2, 3];
console.log(Array.isArray(arr)); // true

let obj = { a: 1, b: 2 };
console.log(Array.isArray(obj)); // false

16. Что делает оператор расширения?

Что делает оператор расширения?

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

В функциях, где ожидаемое количество аргументов для вызова равно нулю или более:

function sum(a, b, c) {
 return a + b + c;
}

let numbers = [1, 2, 3];
console.log(sum(...numbers)); // 6

В литералах массива:

let arr1 = [1, 2, 3];
let arr2 = [...arr1, 4, 5]; 
console.log(arr2); // [1, 2, 3, 4, 5]

В литералах объекта, где количество пар ключ-значение должно быть равно нулю или более:

let arr1 = [1, 2, 3];
let arr2 = [4, 5, 6];
let combined = [...arr1, ...arr2]; 
console.log(combined); // [1, 2, 3, 4, 5, 6]

let obj1 = { a: 1, b: 2 };
let obj2 = { c: 3, d: 4 };
let combinedObj = { ...obj1, ...obj2 }; 
console.log(combinedObj);// { a: 1, b: 2, c: 3, d: 4 }

Для преобразования строки в массив символов:

const str = "JavaScript";
const charArray = [...str]; 
console.log(charArray); // ['J', 'a', 'v', 'a', 'S', 'c', 'r', 'i', 'p', 't']

Для преобразования числа в массив цифр и наоборот:

const num = 12345;
const numArray = [...num.toString()].map(Number); 
console.log(numArray); // [1, 2, 3, 4, 5]

const number = Number(numArray.join(''));
console.log(number); // 12345

Для копирования объектов:

let obj = {
  name: 'Марк',
  age: 25
};

let newobj = { ...obj };
console.log(newobj); // {name: 'Марк', age: 25}

17. Как выполняется клонирование объекта?

Если объект не содержит вложенных объектов, как в приведенном ниже примере, для клонирования можно использовать оператор расширения ... или метод Object.assign():

const obj = {
  firstName: 'Василий',
  lastName: 'Пупкин'  
};
const copy1 = {...obj};
console.log(copy1); // {firstName: 'Василий', lastName: 'Пупкин'}

// или

const copy2 = Object.assign({}, obj);
console.log(copy2); // {firstName: 'Василий', lastName: 'Пупкин'}

Если объект содержит вложенные объекты, нужно выполнить глубокое копирование. Относительно медленный вариант – с использованием JSON:

const obj = {
  data: {
    id: 1
  }
};
const copy = JSON.parse(JSON.stringify(obj));

Другой вариант – с использованием метода cloneDeep из библиотеки lodash:

<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.20/lodash.min.js"></script>

<script>
const original = {
  name: 'Максим',
  details: {
    age: 30  
  }
};

// Вызываем метод Lodash напрямую 
const copy = _.cloneDeep(original); 

copy.name = 'Марина';
copy.details.age = 32;

console.log(original);
console.log(copy); 
</script>

18. Как изменить контекст функции?

Изменить контекст функции можно с помощью методов bind(),call() и apply().

Метод bind() возвращает новую функцию с привязанным контекстом:

function myFunction() {
  return this;  
}

const obj = {name: 'Лев Толстой'};
const newFunction = myFunction.bind(obj);
console.log(newFunction()); // {name: 'Лев Толстой'}

Метод call() принимает последовательность аргументов, а apply() принимает массив аргументов в качестве второго параметра:

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

const numbers = [1, 2];
console.log(sum.call(null, 1, 2)); // 3
console.log(sum.apply(null, numbers)); // 3

19. Что такое тернарный оператор и как он работает?

Что такое тернарный оператор и как он работает?

Тернарный оператор – это сокращенная форма записи if-else. Он называется тернарным, потому что является единственным оператором в JavaScript, который принимает три аргумента. Синтаксис тернарного оператора:

условие ? выражение_если_истинно : выражение_если_ложно

Условие – любое условие, которое возвращает true или false.

Выражение для истинного условия – что нужно вернуть, если условие истинно.

Выражение для ложного условия – что нужно вернуть, если условие ложно.

Например:

let accessAllowed;
let age = 16;

accessAllowed = (age > 18) ? "Доступ открыт" : "Доступ закрыт";

// аналогично записи через if-else:

if(age > 18) {
  accessAllowed = "Доступ открыт";
} else {
  accessAllowed = "Доступ закрыт";  
}
console.log(accessAllowed); // выведет Доступ закрыт

20. Что такое деструктуризация?

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

Деструктуризация массива:

let array = [1, 2, 3];

let [x, y, z] = array; 

console.log(x); // 1
console.log(y); // 2  
console.log(z); // 3

Деструктуризация объекта:

let user = {
  name: "Василий",
  lastname: "Пупкин",
  age: 30
};

let {name, lastname, age} = user;

console.log(name); // "Василий"
console.log(lastname); // "Пупкин"  
console.log(age); // 30

Продолжение следует.

***

Статья по теме

ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ

eFusion
01 марта 2020

ТОП-15 книг по JavaScript: от новичка до профессионала

В этом посте мы собрали переведённые на русский язык книги по JavaScript – ...
admin
10 июня 2018

Лайфхак: в какой последовательности изучать JavaScript

Огромный инструментарий JS и тонны материалов по нему. С чего начать? Расск...