Задачи с собеседований (front-end)
Так получилось, что за свою карьеру front-end разработчика, я побывала на многих собеседованиях. Тема прохождения интервью не теряет своей актуальности, а в комментариях, когда речь заходит о собеседованиях, начинают ломаться копья. Хочу тоже внести свой вклад и поделиться накопившейся коллекцией вопросов. Прошу.
Лирическое отступление
На собеседования меня приглашают охотно, не знаю, виной этому симпатичная фоточка в резюме или его содержание. Если о резюме, то для описания технологий, используемых на каждой текущей работе, я трачу часа два, три. Порой просматриваю в чужие, как они оформлены, да и вдруг найду что-то полезное для себя…
Задачи
Для начала что-нибудь полегче.
1. Есть некоторая строка (var str = ‘fgfggg’;), что будет, если мы возьмем str[0]?
Ответ
str[0] вернет символ строки, который находится по нулевому индексу. Строка в js — immutable, то есть из нее можно прочитать символ, а вот записать нет.
2. Реализовать методы, которые в процессе выполнения строки (2).plus(3).minus(1) дали бы на выходе 4.
Number.prototype.plus = function (value) { return this + value; } Number.prototype.minus = function (value) { return this - value; }
Число два будет доступно через this в функции plus. Из нее мы возвращаем результат сложения числа, на которое указывает this и числа, переданного в качестве аргумента. Аналогично для minus.
3. Сейчас уже редко, но до сих еще спрашивают: «Почему плохо писать прямо в прототипы базовых типов?»
Ответ
Array.prototype.sort = function () {} var t = [2, 1, 22]; t.sort()
Мы рассчитываем, что стандартные методы сработают согласно документации, но какой-то разработчик можем переопределить метод, и он вернет совершенно неожиданный результат.
Именно поэтому библиотека prototype.js уступила jQuery.
4. Дана функция, она принимает в качестве аргументов строки ‘*’, ‘1’, ‘b’, ‘1c’, реализуйте ее так, что бы она вернула строку ‘1*b*1c’
Ответ
function getStr() { return [].slice.call(arguments, 1).join(arguments[0]) }
5. Дано дерево, надо найти сумму всех вершин.
Ответ
Рекурсия.
var sum = 0; function getSum(obj) { sum += obj.valueNode; if (obj.next != null) { for (var i = 0; i < obj.next.length; i++) { getSum(obj.next[i]); } } return sum; } var tree1 = { valueNode: 1, next: [ { valueNode: 3, next: null }, { valueNode: 2, next: null } ] } var tree = { valueNode: 3, next: [{ valueNode: 1, next: null }, { valueNode: 3, next: null }, { valueNode: 2, next: null }, { valueNode: 2, next: [ { valueNode: 1, next: null }, { valueNode: 5, next: null } ] }] }; console.log(getSum(tree1)); sum = 0; console.log(getSum(tree));
Очередь.
function getSum(obj) { var arr = [obj], sum = 0, current; while(arr.length > 0) { current = arr.shift(); sum += current.valueNode; if (current.next != null) { for (var i = 0; i < current.next.length; i++) { arr.push(current.next[i]); } } } return sum; } var tree = { valueNode: 3, next: [{ valueNode: 1, next: null }, { valueNode: 3, next: null }, { valueNode: 2, next: null }, { valueNode: 2, next: [ { valueNode: 1, next: null }, { valueNode: 5, next: null } ] }] }; getSum(tree)
6. Можно ли из js менять значения в before, after?
Давайте, что-нибудь для разгрузки мозга, вопрос на верстку.
7. Вместить три блока 20X20px в ряд, в блок шириной 60px, при этом у блоков должны быть границы.
Ответ
8. Как применяются скругленные углы для элементов и стили для текста(шрифт, тип шрифта, цветащте и тд)?
Для свойства border-radius, наоборот, применяется только к тегу, у которого мы хотим что бы били скругленные углы.
9. Что такое ресет стилей?
К примеру. Мы часто используем список ul для верстки меню, для этого мы каждый раз вынуждены обнулять padding, margin и list-style-type. Можно один раз задать стили, и списки станут без внешней и внутренней границы, а так же без маркеров.
10. Требуется сверстать попап по центру, его размеры нам известны, но мы не хотим что бы он прокручивался вместе со страницей, причем по высоте может и не влезать в высоту экрана.
Ответ
body { overflow: hidden; } .wrap { position: fixed; top: 0; left: 0; width: 100%; height: 100%; overflow-y: auto; background-color: rgba(230, 230, 230, .1); } .popup { position: absolute; width: 400px; height: 300px; right: 0; left: 0; top: 0; bottom: 0; margin: auto; }
11. Нарисовать стилями полукруг.
Ответ
width: 100px; height: 100px; border-right: 1px solid #f00; border-radius: 0 50% 50% 0;
12. Есть массив в котором лежат объекты с датами, отсортировать по датам.
var arr = [{date: '10.01.2017'}, {date: '05.11.2016'}, {date: '21.13.2002'}]; arr.forEach(function(item) { var arrDate = item.date.split('.'), date = new Date(Number(arrDate[2]), Number(arrDate[1]), Number(arrDate[0])); item.time = date.getTime(); }); arr.sort(function (a, b) { if (a.time - b.time < 0) { return false; } else { return true; } }); var res = arr.map(function (item) { return {date: item.date}; }); console.log(res);
13. Есть несколько слов, определить состоят ли они из одних и тех же букв(‘кот’, ‘ток’, ‘окт’)
Ответ
var arr = ['kot', 'tok', 'okt'], arr1 = ['kot', 'tok', 'ott']; function sameWords(arr) { var word1, word2; for (var i = 0; i < arr.length-1; i++) { word1 = 0; word2 = 0; if (arr[i].length !== arr[i+1].length) { return false; } else { for (var j = 0; j < arr[i].length; j++) { word1 += arr[i][j].charCodeAt(0); word2 += arr[i+1][j].charCodeAt(0); } if (word1 !== word2) { return false; } } } return true; } console.log(sameWords(arr)); console.log(sameWords(arr1));
Про промисы
В последнее время часто стали задавать вопросы про promse, что это такое, какие методы обработки ошибок, можно ли обойтись без них.(14, 15, 16)
var promis = new Promise(function (resolve, reject) { ... setTimeout(function () { resolve(res); }, 5000); });
Несколько промисов можно объединить и получить разом ответ от них
var promisesImages = []; for (let i = 0; i < count; i++) { promisesImages.push(new Promise((resolveImg, rejectImg) => { let img = new Image(), startTime = Date.now(); img.src = this.imgUrl + '?' + Math.floor(Math.random() * 100000000); img.onload = () => { resolveImg(Date.now() - startTime); }; img.onerror = rejectImg; })); } Promise.all(promisesImages) .then((timings) => {...})
17. И конечно вопрос на засыпку: «Каким образом можно обойтись без промисов?».
Ответ
18. Еще вспомнился вопрос про обработку ошибок в промисах. У нас есть три запроса к серверу, один возвращает нам имя пользователя, второй его данные, а третий изображение для аватарки, мы для каждого запроса используем по промису, объединяя их в цепочку, что будет если в одном из запросов произойдет ошибка, довыполнится ли цепочка?
Промисы все разрастаются и на смену Callback Hell приходит Promise Hell. Что же делать?
Ответ
function test() { return new Promise(function (resolve) { setTimeout (function () { resolve(1); }) }) } async function test1() { var res = await test(); console.log(res + 1); }
(19)Вот еще один примерчик на асинхронность. Объяснить в какой последовательности выведутся цифры и почему так.
console.log(1); setTimeout(function() { console.log(2); }, 0) console.log(3);
Достаточно популярный, надо сказать. Вот мне он и достался в очередной раз. Уже отработано начала отвечать: «1, 3, 2. Так как хоть js и асинхронен, но внутри его есть очередь выполнения и setTimeout и setInterval, если им указан 0, помещают вызов функции в конец очереди.»
Тут надо сказать ребята зафейлились, заявив, что я ответила правильно, про 132, но не объяснила почему.
(20)Вообще вопросы на setTimeout и setInterval весьма актуальны.Меня спрашивали: «Какое минимальное время можно задать?»
Куда же без замыканий
Не давно выделили целую статью для этого примера(тут), читатели в комментариях расписывали всевозможные способы решения, от традиционных до фантастических. Вот о которых я обычно рассказывала на собеседованиях.
for (var i = 0; i < 10; i++) { setTimeout(function () { console.log(i); }, 100); }
21. Что будет выведено в консоль, как можно модифицировать пример что бы он возвращал правильный результат(назовите как можно больше способов)?
for (var i = 0; i < 10; i++) { (function (i) { setTimeout(function () { console.log(i); }, 100); })(i) }
Не все обращали внимание, что в i можно передать не только контекст
for (var i = 0; i < 10; i++) { setTimeout(function (i) { console.log(i); }.bind(this, i), 100); }
Так же методам setInterval и setTimeout можно передать аргументы, которые будут прокинуты в качестве аргументов калбек-функции
for (var i = 0; i < 10; i++) { setTimeout(function (i) { console.log(i); }, 100, i); }
es6
for (let i = 0; i < 10; i++) { setTimeout(function () { console.log(i); }, 100); }
Давайте что-нибудь похардкорнее.
22. Надо написать функцию, которая вернет «hello world», но при этом в теле функции нельзя использовать ни цифры, ни буквы, а циклы, массивы, объекты можно, но без цифр.
var zero = [].length, one = [{}].length, two = [,,].length, seven = [,,,,,,,].length; console.log(String.fromCharCode(Number(String(seven) + String(two))));
(23) От них же. Числа от 1 до 100 лежат в массиве, они хаотично перемешанные, от туда изъяли одно число, надо найти, что это за число. алгоритм не должен превышать O(n^2) сложности.
Мне пришло в голову более экзотическое решение. Детям и слабонервным лучше не смотреть.
var sum = 101*50, sumArr = eval([4, 2, ... 3, 7].join('+').replace('++', '+')), res; res = sum-sumArr;
Просто мне вспомнилось, что когда-то спрашивали: «Как быстрее всего найти сумму элементов массива?»
eval([4, 2, ... 3, 7].join('+')
Вот на тот момент вспомнилось и не пришло ни чего лучше.
(24) Вот еще фейл-задача. Приведу дословно, те напишу как написали мне.
function Book(name, author) { this.name = name; this.author = author; return this; } function Foo(Book, 'Учебник javascript', 'Петр Сергеев')
Реализовать Foo
function Book(name, author) { this.name = name; this.author = author; return this; } function Foo(Cclass, name, author) { return Object.create(Cclass.prototype); } var book = Foo(Book, 'js', 'petr'); console.log(book.name); -> undefined
Конечно меня не взяли, а на последок посоветовали лучше учить теорию. Хнык-хнык.
В любом случае, я бы предпочла решать так.
function Book(name, author) { this.name = name; this.author = author; return this; } function Foo(Cclass, name, author) { return Cclass.call({}, name, author); } var book = Foo(Book, 'js', 'petr'); console.log(book.name);
Задача про палиндром
(25) Да, до сих пор ее задают. Причем она попалась мне несколько раз. В первый раз я стала решать циклом, вроде бы получилось, только оставалась проблема с разными символами, писать для каждого символа replace — это не вариант, а в регулярках я не сильна, да и сработает он до первого подходящего вхождения. Я не привыкла оставлять за спиной что-то непонятное, поэтому разобралась как реализовать функцию.
function isPalindrom1(str) { if (str.toLowerCase().replace(/[^а-яА-ЯёЁ]/g, '') === str.toLowerCase().replace(/[^а-яА-ЯёЁ]/g, '').split('').reverse().join('')) { return true; } else { return false; } }
Красиво, просто, изящно.
Забавно, но где-то через год мне попался этот же вопрос. Я обрадовалась: «Свезло». Как оказалось, нет. Требовалось решить с помощью цикла, а регулярку можно было использовать только для одного символа. Собеседник мотивировал это тем, что replace с регуляркой по всей строке — слишком ресурсоемко.
Подумав дома, у меня получилось вот так:
function isPalindrom(str) { var str = str.toLowerCase(), lim = str.length - 1, i = 0, j = str.length - 1; while (i <= lim) { if (/[^а-яА-ЯёЁ]/.test(str[i])) { i += 1; } if (/[^а-яА-ЯёЁ]/.test(str[j])) { j -= 1; } if (str[i] != str[j]) { return false; } i += 1; j -= 1; } return true; } console.log(isPalindrom('А роза упала на лапу Азора'));
26. Как подключить js, css? Плюсы, минусы разных способов?
<script></script> <style></style>
прямо на странице или
<script src="script.js"><script> <link rel="stylesheet" href="/css/style.css">
Минус подключения внешних файлов заключается в том, что для их скачки, открываются дополнительные соединения. Может случиться так, что мы не сможем закачать, а если это еще и js файл, подключенный где-то в начале, то мы рискуем показать пользователю пустую страницу.
Размещение стилей и скриптов в самом документе, в этом плане, надежнее. Но при этом скрипты и стили совершенно не кешируются.
27. Чем opacity отличается от visible: hidden и что это такое, отличие от overflow: hidden?
Элемент со стилями visible: hidden так же занимает место, не видим. Но в отличие от элемента с opacity, js-события на нем не срабатывают.
display: none — полностью скрывает элемент, он не видим и не занимает место на странице. javascript не может получить ни width, height.
overflow: hidden; — скрывает все, что попадет за его пределы.
Модное слово — каррирование
28. Реализовать функцию f: f(2, 3) -> 5, при вызове f(2)(3), тоже вернет 5
function f(a, b) { if (b !== undefined) { return a + b; } else { return function (b) { return a + b; } } }
Более сложный вариант.
f(1)(2)(3)() -> 6, f(0)(3)(1)(5)() -> 8
function f(arg) { var value = arg; return function (arg) { if (arg !== undefined) { return f(value + arg); } else { return value; } } }
Реализовать функцию, которая возвращает результаты
foo(1)(2)(3) -> 6
foo(1)(2)(3)(4) -> 10
foo(-1)(2)(3)(4)(7) -> 15
foo(1)(2)(3)…(n) вернет результатом сумму 1+2+3…+n
Самый сложный вариант, когда вызов этой конструкции не заканчивается пустыми скобками, а значит мы не имеем явного указателя на выход.
function foo(value) { var acc = value; function addNext(next) { acc += next; return addNext; } addNext.toString = addNext.valueOf = function() { return acc; } return addNext; }
29. На собесе на прошлую работу мне попался вопрос: ‘Что такое live/die’
В проекте я столкнулась с еще и с bind/unbind
30. JSONP, как реализовать
Все же разработчики нашли некий лайфхак для крос-доменных запросов.
Если вы используете jquery, то всего-то надо указать в параметре «dataType» — «jsonp».
Мало кто знает, что лежит в основе данного запроса, реализация нативным js приблизительно такая
var url = 'http://mydomen.com/news.php', script = document.createElement('script'), callbackName = 'jsonCallback' + Math.randome(); script.src = url + '?callback' + callbackName; window[callbackName] = function(response){ console.log(response); } document.header.appendChild(script);
тег script начинает выкачивать скрипт, лежащий по адресу в атрибуте src, а после закачки, происходит выполнение. Поэтому мы создаем тег script. Случайным образом генерируем имя функции. Формируем урл к ресурсу, где в качестве параметра передается имя нашей новой функции.
В глобальную область, по ключу callbackName помещается функция, которая вызовется, при получение данных от ресурса, они станут доступны внутри через параметр.
<script src='http://mydomen.com/news.php?callback0.90428777...'>
На сервере ресурса сработает что-то в этом роде
echo $_REQUEST[callback] + '(' + json_encode($arDate) + ')';
В window попадет
window.jsonCallback0.90428777 -> function (response) { console.log(response); }
31. Заголовки CORS?
Так же как и с jsonp доступны только GET запросы.
Когда я рассказывала про альтернативу jsonp интервьюер от этого вариант «отмахнулся», сказав, что не надежный. Я с ним не согласна. JSONP легче подменить, чем CORS-заголовки
<script src="http://bla-bla/get?callback=f'></script>
<? echo $GET['callback'].'('.$date.')';
В date подпихнуть вут такую строчку
');alert(document.cookie);'
Сейчас уже исправили, но раньше наблюдалась критическая уязвимость в json-е, с помощью символа переноса строки он подламывался.
32. Ускорение загрузки страницы.
Ответ
2 то же и с css
3 собрать изображения в спрайты
4 кеширование
а) файлы js и css кешируются навсегда( или на длительный период, к примеру на год), когда в них вносятся какие-то изменения, в результате разработки, меняется номер версии, браузер считает, что это новые файлы и закачивает их. Время кеширование содержится в заголовке expires.
б) файл кешируется до тех пор, пока в нем не произошли изменения. Когда пользователь в первый раз заходит на сайт, у него еще нет кеша, файлы закачиваются и в следующий раз, когда происходит обмен заголовками происходит анализ ETag(хеш суммы содержимого содержимого файла или хеш последнего времени изменения, или номер версии) Если он изменился, то закачивается, в противном случае, берется из хеша.
Так же можно брать заголовки с датой последней модификации(Last_Modifed), но это менее надежно.
в) можно сжать данные перед отправкой, решается настройкой конфига в nginx. Пример. Content-Encoding: gzip
г) можно разбить js на модули и на разных страницах подключать по мере необходимости.
Можно найти еще несколько, но они менее популярны или устарели.
33. CommonJS и AMD или модульность?
define([ mod1, mod2, mod3 ], function (Mod1, Mod2, Mod3) { new Mod1(); .... });
define — фактически простая функция, все, что написано за ее приделами будет выполняться без оглядки на то, что происходит в define, поэтому одним аргументом идет callbeck-функция. Как только подгрузятся все зависимости, перечисленные в массиве, так они станут доступны внутри через аргументы callbeck-функции.
Система модулей в Node.Js наоборот синхронна. Мы пишем:
var mod = requaer('modul');
34. Еще интересный пример. Реализовать методы seven, plus, one, five, minus, two. seven(plus(one())) -> 8. five(minus(two())) -> 3
function one(arg){ if (typeof arg === 'function') { return arg(1); } else { return 1; } } function seven(arg) { if (typeof arg === 'function') { return arg(7); } else { return 7; } } function plus(arg) { return function (a) { return a + arg; } }
Аналогично пишутся функции для five, minus, two. Уже дома, поразмышляв в спокойной обстановке, появилось такое решение
function one(arg) { return 1 + (arg || 0); } function two(arg) { return 2 + (arg || 0); } function five(arg) { return 5 + (arg || 0); } function seven(arg) { return 7 + (arg || 0); } function plus(arg) { return arg; } function minus(arg) { return -arg; }
35. Сортировка пузырьком.
Да, да, ее еще спрашивают.
Ответ
var m = [1, 7, 5, 13, 8], count = m.length - 1, max; for (var i = 0; i < count; i++) { for (var j = 0; j < count - i; j++) { if (m[j] > m[j + 1]) { max = m[j]; m[j] = m[j + 1]; m[j + 1] = max; } } }
36. Обратная польская нотация.
Основной принцип. Дана строчка 23+1-, начинаем двигаться по ней, как только доходим до первого арифметического знака, берем две цифры перед ним и на их место, записываем результат вычисления. Получится 51-. Начинаем все с начала.
Кому интересно, вот статья wikipedia
Ответ
var notation = '23+1-', notation1 = '7 2 3 * -', notation2 = '1 2 + 4 * 3 +'; function getComputation(notation) { var queue = [], tmp, num1, num2; for (var i = 0; i < notation.length; i++) { if (/\d+/.test(notation[i]) === true) { queue.push(Number(notation[i])); } else { switch (notation[i]) { case '+': tmp = queue.pop() + queue.pop(); queue.push(tmp); break; case '-': num1 = queue.pop(); num2 = queue.pop(); if (num1 > num2) { tmp = num1 - num2; } else { tmp = num2 - num1; } queue.push(tmp); break; case '*': tmp = queue.pop() * queue.pop(); queue.push(tmp); break; case '/': tmp = queue.pop() / queue.pop(); queue.push(tmp); break; } } } return queue[0]; } console.log(getComputation(notation)); console.log(getComputation(notation1)); console.log(getComputation(notation2));
37. Есть div, в нем другой div, у второго задан padding 50%, как это все будет выглядеть?
.wrap { width: 200px; border: 1px solid green; } .block { width: 200px; padding-bottom: 50%; border: 1px solid red; }
Собеседник предложил мне посмотреть дома как оно сработает, хотя мне стало очень интересно.
Как вы считаете, правильно ли на собеседование отвечать самому на вопрос, если кандидат с ним не справился?
38. Есть строка, состоящая из разных скобок, проверить закрыты ли все. Пример строки: «())({}}{()][][»
function validBraces(str) { var arrOpenSymbols = [], result = false, countOpenSymbols; if (str.length > 0) { for (var i = 0; i < str.length; i++) { if (str[i] === '{' || str[i] === '[' || str[i] === '(') { arrOpenSymbols.push(str[i]); } else { countOpenSymbols = arrOpenSymbols.length; if ((str[i] === '}' && arrOpenSymbols[(countOpenSymbols-1)] === '{') || (str[i] === ']' && arrOpenSymbols[(countOpenSymbols-1)] === '[') || (str[i] === ')' && arrOpenSymbols[(countOpenSymbols-1)] === '(') ) { arrOpenSymbols.pop(); } } } if (arrOpenSymbols.length === 0) { result = true; } else { result = false; } } return result; } console.log(''); console.log(validBraces('()')); console.log(validBraces('[)')); console.log(validBraces('{}[]()')); console.log(validBraces('([{}])')); console.log(validBraces('())({}}{()][]['));
Если бы передавались строки из скобок ‘{‘, ‘[‘, ‘]’, ‘}’
function validBraces(str) { try { eval(str); return true; } catch (err) { return false; } }
39. Напишите код, который при клике на любой div внутри root будет выводить в консоль его id.
<div id="root" style="background: red;"> root <span id="id1" style="background: lightblue;">id1</span> <div id="id2" style="background: green;"> id2 <div id="id3" style="background: yellow;">id3</div> </div> </div>
Ответ
$('#root').on('click', function (event) { event.stopPropogation(); console.log($(event.target).attr('id')); })
40. Напишите код, который сделает из массива объект
// на входе массив
var arr = [ {name: 'width', value: 10}, {name: 'height', value: 20} ]
// на выходе объект
{width: 10, height: 20}
Ответ
function getObj(arr) { var obj = {}; arr.forEach(function(item){ obj[item.name] = item.value; }); return obj; }
41. Что выведется в результате?
var i = 10; var array = []; while (i--) { (function (i) { var i = i; array.push(function() { return i + i; }); })(i); } console.log([ array[0](), array[1](), ])
Ответ
(function (i) {})(i);
создает замыкание, var i = i — уже принадлежат областям видимости в замыканиях.
function() { return i + i; }
42. Есть функция и объект. Напишите все известные вам способы, чтобы вывести в консоли значение x из объекта используя функцию
function f() { console.log(this.x); } var obj = {x: 'bar'};
Ответ
f.call(obj, a, b); f.apply(obj, [a, b]); obj.funk = function f() { console.log(this.x); } obj.funk(); function f() { console.log(this.x); }.bind(obj, a, b); f();
43.
function Book() { this.name = 'foo' } Book.prototype = { getName: function() { return this.name; } }; var book = new Book(); Book.prototype.getUpperName = function() { return this.getName().toUpperCase(); } book.getUpperName();
Что вернет метод?
а) ошибку, т.к. метод объявлен позже, чем создание экземпляра
б) вернется ‘FOO’
44. В js переменные объявленные следующим образом:
a=3; b=«hello»;
Укажите правильные утверждения.
а) создаются в локальном контексте
б) создаются в глобальном контексте
в) создание переменной без ключевого слова var — синтаксическая ошибка.
45. Что вернёт этот код — typeof (function(){})()
а) callback
б) method
в) undefined
г) function
Ответ
47. Почему 0.1 + 0.2 даст 0.30000000000000004
48. Моему коллеге на должность php-разраба, предложили такую
Есть два блока, второй скрытый, если в первом нет дочерних элементов, то показать второй.
<div class="block1"> <div></div> </div> <div class="block2"> <div></div> </div>
.block1 { width: 150px; height: 20px; border: 1px solid red; } .block2 { display: none; width: 100px; height: 50px; border: 1px solid red; }
function showBlock() { var block1 = document.getElementsByClassName('block1')[0], block2 = document.getElementsByClassName('block2')[0]; if (!(block1.childNodes.length > 0 )) { block2.style.display = 'block'; } } document.addEventListener("DOMContentLoaded", function () { showBlock(); });
Конечно это не все задания, которые мне довелось решать, какие-то я благополучно забыла, какие-то слишком банальные, масса вопросов, просто на теорию, остались за кадром.
Прочее
Вот еще парочка вопросов, они на прямую не касаются фронта, но, наверно не лишне знать.
Идеальный интерфейс в вашем представление?
Как вы бы сделали отказоустойчивый сайт?
Какие типы уязвимостей знаете?