Вместе с факультетом Веб-разработки GeekUniversity собрали для вас несколько простых задач и вопросов по JavaScript для обучения и тренировки.
247К открытий257К показов
Вместе с факультетом Веб-разработки GeekUniversity собрали для вас несколько простых задач по JavaScript для обучения и тренировки, а также пару теоретических вопросов. Задачи расположены в порядке возрастания сложности.
Обратите внимание, что у любой задачи по программированию может быть несколько способов решения. Чтобы посмотреть добавленный нами вариант решения, кликните по соответствующей кнопке.
Задача 1
Напишите однострочное решение, которое вычисляет сумму квадратных корней для всех чётных чисел целочисленного массива.
Вариант решения
console.log(
// Входной массив
[1, 4, 3, 0, 4, 5, 4]
// Оставляем только чётные числа
.filter(element => !(element % 2))
// Считаем квадратный корень и записываем в аккумулятор
.reduceRight((accumulator, element) => accumulator + Math.sqrt(element), 0)
); // 6
Метод reduceRight() применяет функцию к аккумулятору и каждому значению массива (справа налево), сводя его к одному значению. А метод reduce() выполняет функцию callback один раз для каждого элемента, присутствующего в массиве, за исключением пустот, принимая четыре аргумента:
— начальное значение (или значение от предыдущего вызова callback);
— значение текущего элемента;
— текущий индекс;
— массив, по которому происходит итерация.
Задача 2
Напишите функцию, которая пишет в консоль число в заданном диапазоне, в случае, если оно успешно делится или не делится с остатком или без остатка в зависимости от параметров.
Вариант решения
function getNumbersModulatordBy(modulus, loggerCallback) {
// Функция, которая возвращает функцию - это подход из функционального программирования
// Называется замыканием (Closure)
return function(start, end) {
loggerCallback({ message: "Конфигурация", config: configuration });
loggerCallback({ message: "Полученный модулятор", modulus: modulus });
loggerCallback({
message: "Полученный start и end",
start: start,
end: end
});
while (start <= end) {
// Стоит обратить внимание:
// 1. При нестрогом равенстве true == 1 и false == 0
// 2. Здесь мы обращаемся к глобальному контексту configuration
// p.s. поле isEntry может меняться
if (start % modulus == configuration.isEntry) {
// В данном случае мы используем loggerCallback как middleware
// Это определит дальнейшую судьбу результата
// Позволяет убрать side-effect
loggerCallback(start);
}
start++;
}
};
}
// Глобальный контекст не имеет блочной видимости
// В данном случае переменная поднимается выше в самое начало кода
// Исполнитель JavaScript видит её в независимости от места инициализации
var configuration = {
modulus: 10,
isEntry: false,
start: 45,
end: 68
};
var loggerCallback = data => console.log(data);
let tenNumbersModulator = getNumbersModulatordBy(
configuration.modulus,
loggerCallback
);
// Переменные с глобальным контекстом доступны из:
// 1. globalThis (в Node.js)
// 2. window (в браузере)
window.configuration.modulus = 5;
let fiveNumbersModulator = getNumbersModulatordBy(
configuration.modulus,
loggerCallback
);
tenNumbersModulator(configuration.start, configuration.end); // 50, 60
tenNumbersModulator(10, 100); // 10, 20, 30, 40, 50, 60, 70, 80, 90, 100
// Так как мы изменили поле isEntry, то теперь:
// Функция вернёт те значения, которые не входят в модуляцию числа
window.configuration.isEntry = true;
fiveNumbersModulator(configuration.start, configuration.end); // 46, 51, 56, 61, 66
fiveNumbersModulator(10, 21); // 11, 16, 21
Задача 3
Есть ферма животных, у всех животных есть имена и возраст. Животные бывают разных типов: Кошки, Собаки, Коровы. У каждого животного могут быть дети. Если животное является родителем этих детей, в свою очередь глубина семейного древа может достигать N либо 0.
Опишите структуры данных для фермы животных и напишите функцию, которая делает подсчёт всех возрастов животных и выводит общий возраст для всей фермы.
Вариант решения
// Родительский класс для всех животных
class Animal {
constructor(name, age, childs = null) {
this.name = name;
this.age = age;
this.childs = childs;
}
}
// Класс Cat - потомок класса Animal
// Имеет те же поля, что и Animal
class Cat extends Animal {
constructor(name, age, childs = null) {
super(name, age, childs);
}
}
// Класс Dog - потомок класса Animal
// Имеет те же поля, что и Animal
class Dog extends Animal {
constructor(name, age, childs = null) {
super(name, age, childs);
}
}
// Класс Cow - потомок класса Animal
// Имеет те же поля, что и Animal
class Cow extends Animal {
constructor(name, age, childs = null) {
super(name, age, childs);
}
}
// Рекурсивная функция для подсчета age
// Обходит все дочерние элементы
function getAnimalsAge(animals) {
let output = 0;
if (animals.length > 0) {
// Использование функции reduce для получения общего age
// https://learn.javascript.ru/array-iteration
output += animals.reduce((acc, current) => {
// Сумма age всех childs
let count = 0;
// Если childs пустой или его нет, тогда нет смысла пробегать по ним
if (current.childs && current.childs.length > 0) {
// Применение рекурсии
count += getAnimalsAge(current.childs);
}
// Возвращаем сумму аккумулятора, текущего животного, сумму age всех childs
return acc + current.age + count;
}, 0);
}
return output;
}
// Функция для получения определённого количества животных
function generateAnimals(type, count) {
let output = [];
for (let i = 0; i <= count; i++) {
let parameter = {
name: `${type} ${i}`,
age: i,
childs: []
};
let item = null;
switch (type) {
case "Cat":
item = new Cat(parameter.name, parameter.age);
break;
case "Dog":
item = new Dog(parameter.name, parameter.age);
break;
case "Cow":
item = new Cow(parameter.name, parameter.age);
break;
}
if (item) {
output.push(item);
}
}
return output;
}
// Добавление childs ко всем переданным животным
function addChildsTo(animals, count, type) {
animals.forEach(animal => {
if (!animal.childs) {
animal.childs = [];
}
// Обратите внимание, что массив - ссылка, поэтому изменяя здесь его поля
// мы меняем их глобально
animal.childs = generateAnimals(type, count);
});
}
let cats = generateAnimals("Cat", 5);
addChildsTo(cats, 10, "Cat");
let dogs = generateAnimals("Dog", 3);
addChildsTo(dogs, 3, "Dog");
let cows = generateAnimals("Cow", 7);
addChildsTo(cows, 1, "Dog");
// Использование оператора Spread (ES6)
// https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Operators/Spread_syntax
let animals = [...cats, ...dogs, ...cows];
console.log(getAnimalsAge(animals)) // 411
Задача 4
Перепишите функцию clone таким образом, чтобы она была способна клонировать переданный как параметр объект.
console.log(cat.name, cat.age); // cat 5
console.log(cat.childs); // [ 'child 1', 'child 2', 'child 3' ]
console.log(animal.name, animal.age); // animal 10
console.log(animal.childs); // [ 'child 1', 'child 2' ]
// Отличный вариант для JSON-safe объектов
let clone = (obj) => JSON.parse(JSON.stringify(obj));
// Опасен для рекурсивных объектов или когда имеется конструктор с параметрами
let clone = obj => {
if (obj === null || typeof obj !== "object" || "isActiveClone" in obj)
return obj;
if (obj instanceof Date) var temp = new obj.constructor();
//or new Date(obj);
else var temp = obj.constructor();
for (var key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
obj["isActiveClone"] = null;
temp[key] = clone(obj[key]);
delete obj["isActiveClone"];
}
}
return temp;
};
Experimental deep copy (экспериментальное глубокое копирование)
Как пишут на Stack Overflow, HTML-стандарт включает в себя алгоритм структурированного клонирования, который может создавать глубокие копии объектов. Он всё ещё ограничен встроенными типами, но в дополнение к тем типам, что поддерживаются в JSON, поддерживает Dates, RegExps, Maps, Sets, Blobs, FileLists, ImageDatas, Sparse Arrays, Typed Arrays и, вероятно, больше в будущем. Решает также проблемы цикличных и рекурсивных структур, которые ломают JSON.
Клонирование вложенных свойств по ссылке пропускается, нужно быть осторожным в использовании.
console.log(cat.name, cat.age); // cat 5
console.log(cat.childs); // [ 'child 1', 'child 2', 'child 3' ]
console.log(animal.name, animal.age); // animal 10
console.log(animal.childs); // [ 'child 1', 'child 2', 'child 3' ]
// Копирует значение всех собственных итерируемых элементов объекта
let clone = (obj) => Object.assign(new Object(), obj);
// Более известен как оператор Spread
let clone = (obj) => { ...obj };
Задача 5
Выйдите из цикла, изменив только две отмеченные строки. Результат в консоли сейчас останавливается на 9 9. Должен на 5 4.
for (let i = 0; i < 10; i++) { //! Эту строку можно изменить
for (let j = 0; j < 10; j++) {
if (i === 5 && j === 5) {
//! Эту строку можно изменить
}
console.log(i, j);
}
}
Вариант решения
Как пишут на MDN web docs, инструкция метки (label) используется вместе с break или continue для альтернативного выхода из цикла. Метка добавляется перед блочным выражением в качестве ссылки, которая может быть использована в дальнейшем.
cycle: for (let i = 0; i < 10; i++) { //! Эту строку можно изменить
for (let j = 0; j < 10; j++) {
if (i === 5 && j === 5) {
break cycle; //! Эту строку можно изменить
}
console.log(i, j);
}
}
Задача 6
Яблоко стоит 1.15, апельсин стоит 2.30. Сколько они стоят вместе – чему равна сумма 1.15 + 2.30 с точки зрения JavaScript?
Ответ
Число хранится в памяти в бинарной форме, как последовательность бит – единиц и нулей. Но дроби, такие как 1.15, 2.30, которые выглядят довольно просто в десятичной системе счисления, на самом деле являются бесконечной дробью в двоичной форме. Это объяснение взято с сайта Современный учебник JavaScript, там же можно подробно почитать про числа в языке.
Все JavaScript-программисты давно привыкли к тому, что typeof null === 'object'; // true, хотя фактически null — примитивное значение. Многие знают, что это баг, и лично Брэндан Айк это признаёт. Этот баг, вероятно, никогда не будет исправлен из-за необходимости сохранения обратной совместимости существующего кода с новыми версиями языка.