Тестовое задание для «Мультибонус» ВТБ
Задача 1. Реализовать метод bind
Постановка задачи:
// Реализовать метод bind
function bind(func, context, args) {
}
let foo = function (args) {
  console.log(this, args);
}
let bar = bind(foo, { a: 1 });
bar([1, 2, 3]); //{a: 1}, [1, 2, 3]
Метод bind служит для того, чтобы создать функцию, в которой будет фиксированный this.
В нашем случае, это будет функция, которая принимает функцию func, context, и аргументы для func. Функция bind
должна вернуть новую функцию, в которой this будет равен переданному context. Эта новая функция будет оберткой над
переданной функцией func.
Решение:
function bind(func, context, args) {
  return function (localArgs = args) { // (*)
    func.call(context, localArgs); // (**)
  }
}
let foo = function (args) {
  console.log(this, args);
}
let bar = bind(foo, { a: 1 }); // (***)
bar([1, 2, 3]); //{a: 1}, [1, 2, 3]
* В функции-обертке создана новая локальная переменная (как аргумент)  localArgs=args. Это сделано для того, чтобы
значение args можно было использовать при вызове func **. Иначе args останется в замыкании при вызове
функции bind ***, и будет равным undefined, так как третий аргумент функции bind не был указан явно.
Мы могли бы явно привязать и аргументы при вызове bind:
let bar = bind(foo, { a: 1 }, [1, 2, 3]);
bar(); //{a: 1}, [1, 2, 3]
Результат вызова bar() останется таким же.
Задача 2. Реализовать метод mapping, аналог метода map
Постановка задачи:
// Реализовать метод mapping, аналог метода map 
let arr = [1, 2, 3];
arr.mapping(function (item, index) {
  console.log(item, index);
})
Метод map создает новый массив с результатом вызова указанной функции для каждого элемента массива.
Мое решение, это функция (mapping), которая получает аргумент func — другую функцию. Внутри mapping создается
новый массив, в который будут пушиться результаты вызова func. Далее циклом проходим по всем элементам массива (чтобы
не привязываться к конкретному массиву, я использовал ключевое слово this).
Для каждого элемента массива мы вызываем переданную функцию func с двумя аргументами: элемент массива, и его индекс.
Результат каждого вызова func пушим в newArray. Когда цикл будет завершен, то есть элементов в массиве this не
останется, мы вернем новый массив newArray.
Решение:
let arr = [1, 2, 3];
arr.mapping = function (func) {
  const newArray = [];
  for (let i = 0; i < this.length; i++) {
    newArray.push(func(this[i], i));
  }
  return newArray;
};
// вызов метода без сохранения нового массива
arr.mapping(function (item, index) {
  console.log(item, index);
});
// вызов метода, и сохранение нового массива в переменную
const arr2 = arr.mapping((item, index) => `item-${index}: ${item}`)
console.log(arr2); // -> ["item-0: 1", "item-1: 2", "item-2: 3"] 
Задача 3. Реализовать метод deepEqual для объектов без JSON.Stringify
Постановка задачи:
// Реализовать метод deepEqual для объектов без JSON.Stringify 
let obj1 = {
  a: 1,
  b: { c: 2 }
}
let obj2 = {
  a: 1,
  b: { c: 2 }
}
console.log(deepEqual(obj1, obj2)); //true
Метод deepEqual(obj1, obj2) сравнивает два объекта и возвращает true, если объекты равны.
Решение:
let obj1 = {
  a: 1,
  b: { c: 2 },
  d: 'string'
  // добавлено свойство d, чтобы убедится,что после проверки
  // вложенного объекта,проверка продолжится, а не вернет
  // результат проверки вложенного объекта
}
let obj2 = {
  a: 1,
  b: { c: 2 },
  d: 'string'
}
function deepEqual(obj1, obj2) {
  if (obj1 === obj2) { // 1
    return true;
  } else if (Object.keys(obj1).length === Object.keys(obj2).length) { // 2
    for (const key in obj1) { // 3
      if (obj2.hasOwnProperty(key)) { // 4
        if (typeof obj1[key] === 'object' && !Array.isArray(obj1[key])) { // 5
          if (!deepEqual(obj1[key], obj2[key])) { // (*)
            return false;
          }
        } else if (obj1[key] !== obj2[key]) { // 6
          return false;
        }
      } else {
        return false;
      }
    }
  } else {
    return false;
  }
  return true; // 7
}
Данный метод не предусматривает ситуации, когда свойством объекта будет массив или функция. Порядок выполнения:
- Если сравниваемые объекты, это один и тот же объект, возвращаем 
true. - Если у объектов равное количество ключей, продолжаем сравнение. Иначе вернем 
false. - Циклом проходимся по всем ключам первого объекта 
obj1. - Проверяем, есть ли текущий ключ объекта 
obj1у объектаobj2. Если нет, вернемfalse, иначе продолжаем. - Если тип текущего свойства равен 
'object'и свойство не является массивом, то рекурсивно вызываем функциюdeepEqual*, передав в нее свойство-объект первого и второго объектов. Если рекурсивный вызов вернулfalse, то завершаем сравнение, вернувfalse. - Если тип текущего свойства — примитив, то просто сравниваем свойства двух объектов и возвращаем 
false, если они не равны. - Когда все ключи и свойства пройдут проверку не вернув 
false, то функцияdeepEqualвозвращаетtrue. 
Задача 4. Реализовать функцию, которая принимает количество элементов и возвращает ряд Фибоначчи
Постановка задачи:
//4) Реализовать функцию, которая принимает количество элементов и возвращает ряд Фибоначчи
//1, 1, 2, 3, 5, 8, 13...
function createFib(n) {
}
console.log(createFib(2)); //1, 1
console.log(createFib(5)); //1, 1, 2, 3, 5
Для решения этой задачи я использовал цикл и массив, в котором изначально есть два элемента [1, 1].
Решение:
function createFib(n) {
  const fibo = [1, 1];
  for (let i = 1; i < n - 1; i++) {
    fibo.push(fibo[i] + fibo[i - 1]);
  }
  return fibo;
}
console.log(createFib(2)); //1, 1
console.log(createFib(5)); //1, 1, 2, 3, 5
Идея заключается в том, что в цикле мы пушим в массив fibo результат сложения последнего элемента и предпоследнего.
Именно поэтому в массиве изначально есть значение [1, 1], чтобы успешно выполнить первую итерацию цикла. Цикл будет
выполняться n - 1 раз, потому что мы считаем индексы массива, счет которых начинается с нуля.
Задача 5. Реализовать функцию, которая проверяет на валидность скобки
Постановка задачи:
//5) Реализовать функцию, которая проверяет на валидность скобки. 
// ()() - валидно
// ()(()) - невалидно (*)
function isValidBrackets() {
}
* Если я правильно понял, то в постановке задачи допустили опечатку, и не валидный пример является валидным. Примеры
не валидных скобок:
// ()(   - не валидно
// (() 	 - не валидно
// ()()) - не валидно
При решении задачи я ориентировался на свой вариант не валидных скобок.
Решение:
function isValidBrackets(str) {
  const leftBracket = '(';
  const rightBracket = ')';
  const arr = [];
  for (let i = 0; i < str.length; i++) {
    if (str[i] === leftBracket) { // (*)
      arr.push(str[i]);
    } else if (arr[arr.length - 1] === leftBracket && str[i] === rightBracket) { // (**)
      arr.pop();
    } else return false;
  }
  return !arr.length; // (***)
}
console.log(isValidBrackets('()()'));   // -> true
console.log(isValidBrackets('()(())')); // -> true
console.log(isValidBrackets('()(()'));  // -> false
console.log(isValidBrackets('())'));    // -> false
Идея решения заключается в том, что циклом проходимся по каждому символу строки, и проверяем:
*если текущий символ, это —'(', то пушим символ в массивarr.**если текущий символ, это —')', при этом последний элемент массива —'(', то удаляем последний символ из массива, иначе возвращаемfalse. (Мы встретили закрывающую скобку раньше открывающей)***после проверки всех символов, мы проверяем длину массива и возвращаемfalse, если в массиве есть элементы. Возвращаемtrue, если элементов в массиве нет.