Нужно рассказать как работает JavaScript-приложение. Каждая вкладка браузера ‒ это отдельное приложение. Рендеринг страницы и интрепретация JavaScript происходит в одном потоке, в основном цикле приложения, а значит ресурсоемких операций следует избегать. Следует упомянуть про событийную модель, про то, что она работает на неблокирующих сокетах (в т.ч.).
Тут еще можно вспомнить про requestAnimationFrame.
Что это (спецификация и язык)? Какая спецификация является текущей (ES 2019)? Какие существуют различия между ними (зачет ‒ вспомнить про const, let, классы, приватные методы, генераторы, Map, Set и т.п.)?
Что такое JSON (JavaScript Object Notation ‒ представление данных, которое является валидным JS кодом)? Какие существуют функции для работы с JSON (JSON.parse, JSON.stringify)? Почему нельзя просто использовать eval? Для чего используется (в этом формате хранятся конфиги, а так же отдается ответ сервера в AJAX-приложениях)?
Что это (Single Page Application)? Как оно работает? Что такое Rest API? Какие есть приницпы проектирования Rest API? Какие есть стандарты (JSON API)? Какие есть альтернативы (SOAP, JSON RPC, GraphQL, Protocol Buffers)?
Задания выполняются на листке/доске, без компа.
Почему всегда нужно использовать строгое сравнение (===
)?
Пример:
0 == false // true
[42] == '42' // и т.п.
Чем плохи typeof и instanceof?
typeof null === "object" // true
42 instnaceof Number // false
Напишите функцию isPlainObject(o)
.
const isPlainObject = o => Object.prototype.toString.call(o) === '[object Object]'
Что такое (Not A Number)? Как получить такое значение ('foo' / 2
)? Как проверить (isNan
)?
typeof NaN // "number" охуенно?
- Чему равен аргумент функции по-умолчанию(undefined)?
- Как проверить является ли значение массивом(Array.isArray)?
- Что будет если поделить число на 0(Вселенная взорвется наху... Infinity вернет)?
- Как проверить является ли число конечным(isFinite)?
- Как проверить является ли число целым(Number.isInteger)?
- Являеются ли строки мутабельными(нет)?
- При передачи строки в функцию она передается по ссылке или по значению(по ссылке)?
Чем отличается передача объекта по ссылке от передачи по значению? Какие объекты передаются по значению?
Задание: напишите самую простую реализацию функции copy(o: <any>)
для копирования объекта произвольной вложенности.
function copy(o) {
return JSON.parse(JSON.stringify(o))
}
Чем отличается var от const и let (у const и let область видимости ограничена фигурными скобками {}
)? Чем отличается const от let (значение, объявленное через const, нельзя присвоить повторно)?
// 1)
{
let x = 42
}
console.log(x) // что выведет?
// 2)
const arr = []
arr.push(42) // Какую ошибку сгенерирует?
Ответы:
- Приведет к ошибке:
Uncaught ReferenceError: x is not defined
- Ошибки не произойдет
Object.freeze(o)
. Не знает и ладно, потом прочитает.
Классика. На любом собеседованиии задают такой вопрос.
Сам вопрос:
Есть такой код:
for (var i = 0; i < 10; i++) {
setTimeout(() => console.log(`i=${i}`), 3000)
}
Что будет выведено?
Правильный ответ: «i=10» 10 раз.
Почему так происходит? Как исправить?
for (let i = 0; i < 10; i++) {
setTimeout(() => console.log(`i=${i}`), 3000)
}
Если справился, то следующий вопрос:
А как бы вы переписали этот код с использованием ES2015:
Правильный ответ:
for (var i = 0; i < 10; i++) {
setTimeout(function (i) {
console.log('i=' + i)
}(i), 3000)
}
Что означает строка 'use strict'
в начале скрипта либо в начале тела функции (включает строгий режим)? Что происходит при его включении (бросаются исключения, например, при попытке присвоить значение ранее необъявленной переменной (в обычном все ок))?
Что выведет этот код?
'use strict'
const test = {
prop: 42,
meth() {
return this.prop
}
}
const fn = test.meth
console.log(fn())
Ответ:
Будет сгенерирована ошибка TypeError. На что ссылается this в этом примере (window)?
Можно спросить почему так происходит (ответ).
Как исправить?
Вариант 1 (его должны обязательно указать):
const fn = test.meth.bind(test)
Вариант 2:
const fn = () => test.meth()
Так же можно спросить чем отличаются стрелочные функции от анонимных и в чем их преимущество (стрелочные функции захватывают контекст this).
Данные две переменные x = 3
и y = 5
.
Как обменять их значения без вспомогательных переменных?
Ответ:
[ x, y ] = [ y, x ]
Правильным так же является и ответ типа такого:
x = x ^ y
y = x ^ y
x = x ^ y
Но он для надмозгов.
Как используются спреды (что это я и сам затруднюсь ответить)?
// При объявлении функции с произвольным количеством аргументов
function foo(...args) {
console.log(args)
}
foo(1, 2, 3) // args будет содержать массив [1, 2, 3]
// Спреды можно использовать при передаче списка аргументов
Math.max(...[3, 5, 1])
// Является альтернативой использованию метода функции apply
Math.max.apply(null, [3, 5, 1])
const [ first, ...rest ] = [1, 2, 3, 4, 5]
console.log(first) // содержит 1
console.log(rest) // [2, 3, 4, 5]
Что такое -/- (функция-обработчик, передаваемая в функцию в качестве аргумента и вызываемая внутри этой фунгкции с передачей ей результата выполнения какой-то асинхронной операции)? Что такое callback-hell (множество вложенных колбеков)? Как избежать его (использовагние промисов)?
Что такое Promise?
‒ Это бъект, которые позволяет обрабатывать результаты асинхронны операций в будущем.
Для чего нужны async/await?
Напишите асинхронную функции sleep вида:
function sleep(timeout) {
[some code]
}
// Пример использования
async run() {
console.log('Ждем 3 секунды')
await sleep(3000)
console.log('Продолжаем работу...')
}
Решение:
function sleep(timeout) {
return new Promise((resolve, reject) => setTimeout(resolve, timeout))
}
- В: Что такое генераторы?
- О: Это функции, которые приостанавливают свое выполнение в определенной точке и при последующих вызовах продолжает выполнение с этой точки.
Общий вид:
function* g() {
yield 'foo'
yield 'bar'
yield 'baz'
}
Что возвращает функция генератор (итератор)? Что делает оператор yield (приостонавливает выполнение функции и возвращает значение, но в отличии от return при последующем вызове функции продолжает работу с места остановки)? С помощью какого метода можно получить текущее значение итератора (next)? Что он возвращает(объект вида: { value: <any>, done: <bool> }
)?
Какая конструкция используется для цикла по итератору?
Ответ:
for (let it of g()) {
// ...
}
Задание: напишите с использование генераторов функцию, возвращающую числа Фиббоначи.
Ряд Фиббоначи выглядит следующим образом:
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, …
В нем каждое последующее число равно сумме двух предыдущих.
Решение:
function* fib() {
let [ cur, next ] = [ 0, 1 ]
for (;;) {
yield cur
;[ cur, next ] = [ next, cur + next ]
}
}
‒ Возвращает Promise и делает запрос.
В чем ее преимущество по сравнению с XMLHttpRequest (коротко: более богатый API)?
Нет ни одного js-кодера, который не пытался написать свой eventEmitter.
Напишите class EventEmitter:
class EventEmitter {
// ...
on(event, listener) {
// ...
}
off(event, listener) {
// ...
}
emit(event, ...args) {
// ...
}
}
const events = new EventEmitter()
events.on('greeting', name => {
console.log(`Hello ${name}!`)
})
events.emit('greeting', 'Sergey')
Решение:
class EventEmitter {
// Приватные методы (NEW)
// Используем Map вместо Plain Object,
// так как у первого ключами могут быть любые объекты, а не только строки +
// не нужна проверка hasOwnProperty
#listeners = new Map()
on(event, listener) {
let listeners = this.#listeners.get(event)
if (!listeners) {
// Используем Set вместо Array, чтобы нельзя было добавить один и тот же
// обработчик два раза
listeners = new Set()
this.#listeners.set(event, listeners)
}
listeners.add(listener)
return this
}
off(event, listener) {
const listeners = this.#listeners.get(event)
if (listeners) {
listeners.delete(listener)
// Если листенеров больше не осталось, то нужно удалить ключ
if (listeners.size === 0) {
this.#listeners.delete(event)
}
}
return this
}
emit(event, ...args) {
const listeners = this.#listeners.get(event)
if (listeners) {
for (const listener of listeners) {
listener(...args)
}
}
return this
}
}
Еще вопросы:
- Для чего нужен super? Как вызвать родительский конструктор(
super([args])
в методеconstruct
класса наследника)? Как обратиться к родительскому методу(super.meth
)? - Что такое геттеры и сеттеры?
- Как происходит наследование (extends)?
- Как сделать метод статическим (static)?
Что такое cookies (небольшие порции данных, которые хранятся на стороне клиента)?
Как установить cookie со значением foo=bar
на неопределенный срок?
document.cookie = 'foo=bar'
Как получить все куки в виде объекта (ассоциативного массива)?
// Такой вариант
const c={}
document.cookie.split('; ').forEach(pair => {
const [ k, v ] = pair.split('=')
c[decodeURIComponent(k)] = decodeURIComponent(v)
})
console.log(c)
// Тру функицональщина
document.cookie.split('; ').reduce((acc, pair) => {
const [ k, v ] = pair.split('=', 2).map(decodeURIComponent)
acc[k] = v
return acc
}, {})
// С циклами не айс
Как еще можно хранить сессионные данные на стороне клиента (localStorage)?
Что такое JWT?
JSON Web Tokens ‒ токены, хранящиеся на стороне клиента. Такой токен состоит из трех частей: заголовков, тела и сигнатуры. В теле хранятся сессионные данные (обычно идентефикатор пользователя). Сигнатуру нельзя подделать не зная секретного ключа (который хранится на сервере).
Какие ключевые слова используется при работе с модулями (import/export)?
Нужно привести примеры:
// Если функция экспортируется так
export const func = () => {}
// То ее импорт выглядит так
import { func } from './lib'
// Можно импортировать под другим именем
import { func as name } from './lib'
// Можно импортировать все под определенным неймспейсом
import { * as lib } from './lib'
// Нужно упомянуть про export default
export default ...
// При его использование импортировать можно только так
import lib from './lib'
Можно ли использовать import/export в браузере без компиляторов типа Babel?
‒ Да.
<script type="module" src="main.js">
В main.js можно испортировать уже привычным образом.
‒ Это представление документа в виде дерева узлов. Каждый узел представляет собой элемент определенного типа (элемент, текст, комментарий, атрибут, документ, фрагмент и т.д.). Узлы содержат ссылки на детей и родителей.
О: [ Document | Element ].querySelector
и [ Document | Element ].querySelectorAll
.
<!doctype html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
</head>
<body>
<table>
<tr>
<td>First</td>
<td>Second</td>
<td>Third</td>
</tr>
</table>
</body>
</html>
Ответ:
document.querySelector('td:nth-child(2)')
А как сделать тоже самое, но без querySelector?
document.getElementsByTagName('td')[1]
Что такое виртуальный DOM? Суть: позволяет избежать повторного построения DOM-дерева, если данные (модель) не изменились.
Что это? ‒ Это механизм, позволяющий синхронизировать изменения модели и представления.