Я начал менять работу в конце прошлого года, и теперь я пришел к выводу, что я периодически брал интервью во многих компаниях, оглядываясь назад, я испытал то, что меня разрывал интервьюер, я злился на письменные вопросы теста и почти оставаясь, когда мне было грустно.Слезы без техники.
В этой статье я планирую сделать краткий обзор различных вопросов, с которыми я столкнулся при поиске работы (я буду обобщать после каждого собеседования), и наиболее интересных вопросов, с которыми я столкнулся в своем собственном обзоре.Помогите друзьям.
Давайте сначала поговорим о сложности этих вопросов, большинство из них являются базовыми вопросами, потому что этот опыт дает мне ощущение, что независимо от того, проходите ли вы собеседование для продвинутого или начального уровня, базовые знания обязательно будут заданы и даже имеют определенную глубину. , поэтому базовые знания по-прежнему очень важны.
Я разобью его на несколько статей в зависимости от типа:
Резюме интервью: Резюме пунктов интервью JavaScript(завершенный)
Резюме интервью: краткое изложение пунктов интервью nodejs(завершенный)
Резюме интервью: сводка точек интервью, связанных с браузером(завершенный)
Резюме интервью: Резюме пунктов интервью CSS(завершенный)
Резюме интервью: краткое изложение моментов интервью, связанных с framework vue и проектированием.(завершенный)
Резюме интервью: Резюме нетехнических вопросов(завершенный)
Я найду время, чтобы завершить незаконченное резюме~
эта статья правильнаяnodejsСвязанные темы обобщаются, и друзья могут сначала собрать их.
сначала посмотрите каталог
В: Как вы видите, что nodejs может поддерживать высокий уровень параллелизма?
Этот вопрос включает в себя несколько аспектов, это хороший плюс для хорошего разговора. Следуйте инструкциям ниже, чтобы объяснить интервьюеру
- Модель однопоточной архитектуры nodejs
Фактически, nodejs не является настоящей однопоточной архитектурой, потому что nodejs также имеет потоки ввода-вывода (сетевой ввод-вывод, дисковый ввод-вывод), и эти потоки ввода-вывода состоят из потоков ввода-вывода более низкого уровня.libuvОбработка этой части потока прозрачна для разработчика. Код JavaScript всегда работает на V8 и является однопоточным.
Таким образом, с точки зрения разработчика, nodejs является однопоточным.
Вот фото сети:
Обратите внимание, что в правой части изображения есть цикл событий, ключевые моменты которого будут обсуждаться далее.
Преимущества и недостатки однопоточной архитектуры:
Преимущество:
- Один поток воспроизводится в одном потоке, что устраняет накладные расходы на переключение между потоками.
- Существует также проблема синхронизации потоков, и не нужно беспокоиться о проблеме конфликта потоков.
Недостаток:
- Недостаток тоже очевиден, теперь он запускается с 4 ядрами, и один поток не может полностью использовать ресурсы процессора.
- Один поток, как только он падает, приложение зависает, и все, кто отлаживает скрипт, также знают, что как только процесс выполнения сообщит об ошибке, эта отладка сразу закончится
- Поскольку можно использовать только один процессор, как только процессор занят определенным расчетом, процессор не будет освобожден, а последующие запросы будут все время приостановлены, и прямого ответа не будет.
Конечно, эти недостатки имеют зрелое решение, используя процесс управления PM2, или на K8S также может
- Ядро: механизм цикла событий
Так как же ваш единственный поток поддерживает высокий параллелизм?
Ядро лежит в механизме циклов событий js-движка (я думаю, что это открытие довольно хорошо)
Циклы событий браузеров и nodejs немного отличаются.Во-первых, позвольте интервьюеру кратко описать ядро цикла событий, стек выполнения, очередь макросов и микроочередь.Для конкретного введения, пожалуйста, обратитесь к резюме, которое я написал ранее.цикл событий js
Затем я сосредоточусь на разнице между циклом событий nodejs. Я не хочу смешивать два вопроса, поэтому это отдельный вопрос. Я подробно объясню ответ на следующий вопрос.
- Сделайте вывод, что nodejs является асинхронным и неблокирующим, поэтому он может выдерживать высокий уровень параллелизма.
Вот каштаны:
Например, когда клиент запрашивает вход A, ему нужно прочитать файл, и после чтения файла содержимое интегрируется, и, наконец, данные возвращаются клиенту. Но когда при чтении файла приходит другой запрос, каков поток обработки?
Художник души, я нарисовал картину, всем понятно
- Запрос A поступает на сервер, и поток начинает обработку запроса.
- Запрос A должен прочитать файл, ок, и передать его в файловый IO для обработки, но обработка относительно медленная и занимает секунды 3. В это время запрос A зависает (это слово может быть неподходящим), ожидание уведомления, а реализация ожидания реализована механизмом цикла событий,
- Когда A запрашивает ожидание, процессор освобождается.В это время приходит запрос B, и процессор обрабатывает запрос B.
- Между двумя запросами нет конкуренции. Когда произойдет блокировка запросов? Когда задействовано большое количество вычислений, потому что вычисление выполняется на движке js, стек выполнения застревает, и другие функции не могут быть выполнены, например, построить большой объект с очень глубоким уровнем и повторить этот объект.
JSON.parse(JSON.stringify(bigObj))
- Если у вас есть возможность, вы можете расширить понятия синхронизации, асинхронности, блокировки и неблокировки на интервьюера.
Синхронный и асинхронный фокус на механизмах передачи сообщений.
-
Синхронизация: после инициации вызова вызов не возвращается до тех пор, пока не будет получен результат, и выполняется до тех пор, пока вызов не вернется, то есть вызывающий объект ждет, пока вызываемый объект вернет результат.
-
Асинхронный: после инициации вызова вызов возвращается напрямую, не ожидает результата и продолжает выполняться, а результат выполнения уведомляется вызывающей стороне вызываемым пользователем через состояние, уведомление и т. д. Типичная модель асинхронного программирования например Node.js
Блокирующие и неблокирующие фокусируются на состоянии потока во время ожидания результата.
- Блокировка: в ожидании результата вызова поток зависает и не выполняется
- Неблокирующий: в отличие от вышесказанного, текущий поток продолжает выполняться вниз.
Использованная литература:Ууху. Call.com/question/19… zhuanlan.zhihu.com/p/41118827
Q: Представьте цикл событий nodejs
Здесь предполагается, что вы уже имеете представление о цикле событий браузера, как показано на следующем рисунке:
Как показано на рисунке выше, цикл обработки событий подразделяется на эти шесть этапов следующим образом:
-
Timers: Событие обратного вызова Timer Interval Timeout, функция обратного вызова таймера будет выполняться по очереди. -
Pending: на этом этапе будут выполняться некоторые обратные вызовы системного уровня. -
Idle,prepare: Этот этап "только для внутреннего использования" -
Poll: функция обратного вызова ввода-вывода, этот этап важнее и сложнее, -
Check: выполнить обратный вызов setImmediate() -
Close: выполнить обратный вызов события закрытия сокета.
Стадии развития, требующие взаимоотношений
Три фазы, связанные с нашим развитием:Timers Poll Check
Timers: Выполнить обратный вызов таймера, но обратите внимание, что перед узлом 11 несколько последовательных обратных вызовов таймера будут выполняться непрерывно, вместо выполнения макрозадачи и немедленного выполнения микрозадачи, как в браузере.
Check: на этом этапе выполняется обратный вызов setImmediate(), это событие существует только в nodejs.
Poll: Запуск двух вышеуказанных этапов фактически запускается на этапе опроса, и порядок выполнения этапа опроса следующий.
- Сначала проверьте, есть ли событие на этапе проверки, и выполните его, если оно есть.
- После выполнения фазы проверки проверьте, есть ли событие в очереди фазы опроса, и выполните, если оно есть.
- После выполнения очереди опроса выполняются события фазы проверки.
В nodejs также есть макро-задачи и микро-задачи, за исключением того, что в nodejs их гораздо больше.process.nextTick, классификация макрозадач и микрозадач согласована.
Итак, когда выполняются микрозадачи?
На картинке выше желтые этапы находятся рядом с небольшим блоком.microtask, события в очереди микрозадач выполняются сразу после выполнения каждого этапа.
Ниже приведено описание каштана.
каштаны в микро очереди
Следующий код:
const fs = require('fs');
const ITERATIONS_MAX = 3;
let iteration = 0;
const timeout = setInterval(() => {
console.log('START: setInterval', 'TIMERS PHASE');
if (iteration < ITERATIONS_MAX) {
setTimeout(() => {
console.log('setInterval.setTimeout', 'TIMERS PHASE');
});
fs.readdir('./image', (err, files) => {
if (err) throw err;
console.log('fs.readdir() callback: Directory contains: ' + files.length + ' files', 'POLL PHASE');
});
setImmediate(() => {
console.log('setInterval.setImmediate', 'CHECK PHASE');
});
} else {
console.log('Max interval count exceeded. Goodbye.', 'TIMERS PHASE');
clearInterval(timeout);
}
iteration++;
console.log('END: setInterval', 'TIMERS PHASE');
}, 0);
// 第一次执行
// START: setInterval TIMERS PHASE
// END: setInterval TIMERS PHASE
// setInterval.setImmediate CHECK PHASE
// setInterval.setTimeout TIMERS PHASE
// 第二次执行
// START: setInterval TIMERS PHASE
// END: setInterval TIMERS PHASE
// fs.readdir() callback: Directory contains: 9 files POLL PHASE
// fs.readdir() callback: Directory contains: 9 files POLL PHASE
// setInterval.setImmediate CHECK PHASE
// setInterval.setTimeout TIMERS PHASE
// 第三次执行
// START: setInterval TIMERS PHASE
// END: setInterval TIMERS PHASE
// setInterval.setImmediate CHECK PHASE
// fs.readdir() callback: Directory contains: 9 files POLL PHASE
// setInterval.setTimeout TIMERS PHASE
process.nextTick
Что касается process.nextTick, то приоритет этого события выше, чем у событий других микроочередей, поэтому для callback-событий, которые нужно выполнить немедленно, событие можно поместить в начало микроочереди через этот метод.
Следующий код:
Promise.resolve().then(function () {
console.log('promise1')
})
process.nextTick(() => {
console.log('nextTick')
process.nextTick(() => {
console.log('nextTick')
process.nextTick(() => {
console.log('nextTick')
process.nextTick(() => {
console.log('nextTick')
})
})
})
})
// nextTick=>nextTick=>nextTick=>timer1=>promise1
Отличие от результата выполнения цикла событий браузера
Давайте посмотрим на результаты выполнения следующего кода в браузере и nodejs соответственно.
setTimeout(() => {
console.log('timer1')
Promise.resolve().then(function() {
console.log('promise1')
})
}, 0)
setTimeout(() => {
console.log('timer2')
Promise.resolve().then(function() {
console.log('promise2')
})
}, 0)
Друзья, знакомые с очередями событий браузера, скоро узнают浏览器中 timer1->promise1->timer2->promise2, в браузере очередь микрозадач выполняется сразу после выполнения каждой макрозадачи.
Так что насчет nodejs?
Результат таков:timer1->timer2->promise1->promise2, потому что очередь микрозадач выполняется сразу после завершения каждого этапа, поэтому этап таймера имеет два события обратного вызова.После того, как события выполняются последовательно, события в микроочереди выполняются перед переходом к следующему этапу.
Примечание: этот результат находится вnode 10и следующие версии были протестированы, изменены в версиях 11 и выше, и результаты выполнения согласуются с результатами выполнения браузера.
timer1->promise1->timer2->promise2
Справочная статья:
Вопрос: Как nodejs создает потоки процессов и в каких сценариях его можно использовать?
Как запустить несколько дочерних процессов
Одним из недостатков однопоточности является то, что многоядерность не может быть использована полностью, поэтому официальный запускclusterмодуль, модуль кластера может создавать дочерние процессы, которые совместно используют порты сервера
const cluster = require('cluster');
for (let i = 0; i < numCPUs; i++) {
cluster.fork(); // 生成新的工作进程,可以使用 IPC 和父进程通信
}
по существу черезchild_process.fork()Он специально используется для порождения нового процесса Node.js.Порожденный дочерний процесс Node.js не зависит от родительского процесса, за исключением канала связи IPC, установленного между ними.Каждый процесс имеет свою собственную память со своим собственным экземпляром V8.
Как запустить несколько потоков на основе одного процесса
В nodejs 10.0 и выше добавленоworker_threadsмодуль, который может открывать несколько потоков
const {
Worker, isMainThread, parentPort, workerData
} = require('worker_threads');
const worker = new Worker(__filename, {
workerData: script
});
- Как передавать данные между потоками:
parentPortpostMessageonОтправка сообщений о прослушивании - Общая память:
SharedArrayBufferчерез эту общую память
сцены, которые будут использоваться
- В общем сценарии, если вам нужно выполнить команды оболочки в службе, вам нужно запустить процесс
var exec = require('child_process').exec;
exec('ls', function(error, stdout, stderr){
if(error) {
console.error('error: ' + error);
return;
}
console.log('stdout: ' + stdout);
});
- Для служб, требующих большого объема вычислений, рабочий поток может быть запущен и выполнен этим потоком, а результат будет уведомлен служебному потоку после завершения выполнения.
Ссылка на ссылку:Я узнаю, способность /node будет /node...
Q: Реализация и принцип луковой модели koa2
В настоящее время популярен фреймворк nodejs koa2, кода этого фреймворка не так много, и он очень прост для понимания, всем рекомендую взглянуть.
Спросите про koa2, только уточните про его core-onion модель.
Это очень простой коа-сервер
const Koa = require('koa');
const app = new Koa();
app.use(async (ctx, next) => {
ctx.body = 'Hello World';
console.log('firsr before next')
next()
console.log('firsr after next')
});
app.use(async (ctx, next) => {
console.log('sencond before next')
next()
console.log('sencond after next')
ctx.body = 'use next';
});
app.listen(3500, () => {
console.log('run on port 3500')
});
проситьhttp://127.0.0.1:3500/выход
firsr before next
sencond before next
sencond after next
firsr after next
Инициализировать промежуточное программное обеспечение
пройти черезapp.useМетод помещает промежуточную функцию в массив. Шаги следующие:
-
Определите, является ли промежуточная функция генератором или нет
generators, текущая асинхронная схема, используемая koa2,async/await,еслиgeneratorsфункция, которая преобразуется вasync/await -
Использование массива хранения промежуточного программного обеспечения
use(fn) {
if (typeof fn !== 'function') throw new TypeError('middleware must be a function!');
if (isGeneratorFunction(fn)) {
deprecate('Support for generators will be removed in v3. ' +
'See the documentation for examples of how to convert old middleware ' +
'https://github.com/koajs/koa/blob/master/docs/migration.md');
fn = convert(fn);
}
debug('use %s', fn._name || fn.name || '-');
this.middleware.push(fn);
return this;
}
Промежуточное программное обеспечение выполнения (луковая модель)
Мы регистрируем промежуточное ПО через использование. Функция промежуточного ПО имеет два параметра. Первый — это контекст, а второй — следующий. Во время выполнения функции промежуточного ПО, если встречается next(), она войдет в следующее промежуточное ПО. следующее промежуточное выполнение завершено, он возвращается к предыдущему промежуточному программному обеспечению для выполнения метода после next(), что является логикой выполнения промежуточного программного обеспечения.
Основные функции следующие, я добавил комментарии
// koa-compose/index.js
function compose(middleware) {
// middleware 函数数组
if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array!')
for (const fn of middleware) {
if (typeof fn !== 'function') throw new TypeError('Middleware must be composed of functions!')
}
/*
content:上下文
next:新增一个中间件方法,位于所有中间件末尾,用于内部扩展
*/
return function (context, next) {
// last called middleware #
let index = -1 // 计数器,用于判断中间是否执行到最后一个
return dispatch(0) // 开始执行第一个中间件方法
function dispatch(i) {
if (i <= index) return Promise.reject(new Error('next() called multiple times'))
index = i
let fn = middleware[i] // 获取中间件函数
if (i === middleware.length) fn = next // 如果中间件已经到了最后一个,执行内部扩展的中间件
if (!fn) return Promise.resolve() // 执行完毕,返回 Promise
try {
// 执行 fn ,将下一个中间件函数赋值给 next 参数,在自定义的中间件方法中显示的调用 next 函数,中间件函数就可串联起来了
return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
} catch (err) {
return Promise.reject(err)
}
}
}
}
Логику функций понять несложно, но магия кроется в дизайне.Глядя на официальную картинку, очень умно использовать идею функционального программирования (если вы знакомы с функциональным программированием, вы можете дать интервьюеру волна)
Q: Представьте поток
Потоки широко используются в nodejs, но для большинства разработчиков они больше используют потоки, такие как ответ на запрос в HTTP, стандартный ввод и вывод, чтение файлов (createReadStream), инструменты сборки gulp и т. д.
Поток можно понимать как канал, например, чтение файла.Общим методом является чтение с жесткого диска в память, а затем чтение из памяти.Этот метод не представляет проблемы для небольших файлов, но если это большой файл, эффективность низкая.Очень низкая, и может не хватать памяти.Использование потокового метода похоже на вставку соломинки в большой файл, непрерывное чтение содержимого файла понемногу, и другой конец трубы может обрабатывать данные после получения данных.Понимание Друзья Linux должны быть хорошо знакомы с этой концепцией.
В Node.js существует четыре основных типа потоков:
- Writable — поток, в который можно записывать данные (например, fs.createWriteStream()).
- Readable — поток, из которого данные могут быть прочитаны (например, fs.createReadStream()).
- Duplex — доступный для чтения и записи поток (например, net.Socket).
- Transform — дуплексный поток, который может изменять или преобразовывать данные во время чтения и записи (например, zlib.createDeflate()). Те, у кого больше контактов, - это первый и второй. канал для потребления читаемых потоков
const fs = require('fs');
// 直接读取文件
fs.open('./xxx.js', 'r', (err, data) => {
if (err) {
console.log(err)
}
console.log(data)
})
// 流的方式读取、写入
let readStream = fs.createReadStream('./a.js');
let writeStream = fs.createWriteStream('./b.js')
readStream.pipe(writeStream).on('data', (chunk) => { // 可读流被可写流消费
console.log(chunk)
writeStream.write(chunk);
}).on('finish', () => console.log('finish'))
Модуль потока предоставляется изначально. Вы можете прочитать официальную документацию. API очень мощный. Если нам нужно создать новый поток, нам нужно использовать этот модуль.
Рекомендуемые документы:javascript.ruanyifeng.com/node будет/body hot…
узел будет .capable/api/stream. …
В: Какова реализация резки бревен nodejs
использоватьwinstonа такжеwinston-daily-rotate-fileРеализуйте управление и резку бревен, ежедневную резку и резку по размеру.
(Конкретная реализация тщательно не изучена, и заинтересованные друзья могут посмотреть исходный код)
Q: Связь между битами и байтами
Бит: бит означает двоичный Байт: 1 байт = 8 бит
В: О кодировке символов
ASCII: нормативный стандарт для кодирования
Юникод: все символы мира включены в набор. Пока компьютер поддерживает этот набор символов, он может отображать все символы, и не будет искаженных символов. Юникод является надмножеством ASCII.
UTF-32 UTF-8 UTF-16все кодировки Unicode
UTF-32: использует четыре байта фиксированной длины для представления каждой кодовой точки.
UTF-8: Используйте байты переменной длины для представления каждой кодовой точки.Если может быть представлен только один байт, используйте один байт, если одного недостаточно, используйте два... Итак, в кодировке UTF-8 Далее символ может состоят из 1-4 байт.
UTF-16: сочетание фиксированной длины и переменной длины, он имеет только два байта и четыре байта для представления кодовых точек.
Q: Процесс выполнения установки npm
Ниже приводится сводка цитируемых пользователей сети, см. ссылку в конце статьи.
механизм установки модуля npm
- Выполните команду установки npm
- Запросить, существует ли уже указанный модуль в каталоге node_modules.
- Если он существует, не переустанавливайте
- если не существует
- npm запрашивает в реестре URL-адрес сжатого пакета модуля.
- Загрузите сжатый пакет и сохраните его в каталоге .npm в корневом каталоге.
- Разархивируйте сжатый пакет в каталог node_modules текущего проекта.
Как работает нпм
войтиnpm installПосле команды и нажатия Enter он пройдет следующие этапы (в качестве примера возьмем npm 5.5.1):
- Выполните предустановку самого проекта. Если текущий проект npm определяет хук предустановки, он будет выполнен в это время.
- Чтобы определить модули зависимостей первого уровня, первое, что вам нужно сделать, это определить зависимости первого уровня в проекте, то есть модули, непосредственно указанные в свойствах dependencies и devDependencies (при условии, что параметр npm install не добавлено на данный момент). Сам проект является корневым узлом всего дерева зависимостей. Каждый модуль зависимостей первого уровня представляет собой поддерево под корневым узлом. npm запустит несколько процессов, чтобы постепенно найти более глубокие узлы из каждого модуля зависимостей первого уровня.
- Получение модуля, получение модуля - это рекурсивный процесс, который делится на следующие этапы:
- Получить информацию о модуле. Перед загрузкой модуля необходимо сначала определить его версию, потому что package.json часто является семантической версией (semver, семантическая версия). На данный момент, если в файле описания версии (npm-shrinkwrap.json или package-lock.json) есть информация о модуле, вы можете получить ее напрямую, если нет — получить со склада. Если версия пакета в packageaeg.json ^1.1.0, npm отправится в репозиторий, чтобы получить последнюю версию в виде 1.x.x.
- Получите сущность модуля. На предыдущем шаге будет получен адрес сжатого пакета (resolved field) модуля, и npm будет использовать этот адрес для проверки локального кеша, если он есть в кеше, то он будет взят напрямую, если нет, то он будут загружены со склада.
- Найдите зависимость модуля, если есть зависимость, вернитесь к шагу 1, если нет, остановитесь.
-
Установите модуль, этот шаг обновит
node_modules, и выполнять функции жизненного цикла в модуле (в порядке предварительной установки, установки, постустановки). -
Выполнить жизненный цикл самого проекта.Если текущий проект npm определяет хуки, он будет выполнен в это время (в порядке установки, постустановки, предварительной публикации, подготовки).
Последним шагом является создание или обновление файла описания версии, и процесс установки npm завершен.
Сведение модулей (дедупликация)
В интернете есть прикол, курьер npm: приезжает твой node_modules, открываешь дверь, а там куча посылок
На предыдущем шаге вы получаете полное дерево зависимостей, которое может содержать множество повторяющихся модулей. Например, модуль A зависит от loadsh, а модуль B также зависит от lodash. До npm3 он будет устанавливаться строго по структуре дерева зависимостей, поэтому вызовет избыточность модулей.
По умолчанию было добавлено с npm3dedupeпроцесс. Он будет проходить по всем узлам и размещать модули один за другим под корневым узлом, то естьnode-modulesпервый слой. При обнаружении повторяющихся модулей они отбрасываются.
Здесь требуется определение повторяющихся модулей, что означает, что модули имеют одинаковые имена и частично совместимы. Каждый semver соответствует диапазону допустимых версий.Если допустимые диапазоны версий двух модулей перекрываются, совместимая версия может быть получена без необходимости иметь точно такие же номера версий, что позволяет удалить больше избыточных модулей в процессе дедупликации. .
Например, в node-modules модуль foo зависит от lodash@^1.0.0, а модуль bar зависит от lodash@^1.1.0, поэтому ^1.1.0 является совместимой версией.
И когда foo зависит от lodash@^2.0.0, а bar зависит от lodash@^1.1.0, согласно правилам semver, между ними нет совместимой версии. поместит одну версию в node_modules, другая останется в дереве зависимостей.
Например, предположим, что изначально дерево зависимостей выглядело так:
node_modules -- foo ---- lodash@version1
-- bar ---- lodash@version2
Предполагая, что версия1 и версия2 являются совместимыми версиями, после дедупликации она примет следующий вид:
node_modules -- foo
-- bar
-- lodash(保留的版本为兼容版本)
Предполагая, что version1 и version2 являются несовместимыми версиями, более поздние версии остаются в дереве зависимостей:
node_modules -- foo -- lodash@version1
-- bar ---- lodash@version2
Цитируемая статья:Как дерево Способность /вопрос/к…
резюме
ВышеnodejsСоответствующие резюме будут по-прежнему дополняться при столкновении с репрезентативными темами в будущем.
Если в статье что-то не так, поправьте меня.
Спасибо всем~