Резюме более дюжины вопросов для интервью с полным стеком на крупных фабриках

внешний интерфейс опрос

Эта статья включена в ежедневный вопрос GitHub:DailyQuestion, включая возможность продвижения по службе на большой фабрике, личную книгу и несколько вопросов для интервью, изучение по пять минут в день и поступление на большую фабрику на один год.

01 Что такое антивибрация и дросселирование и каковы сценарии их применения

Общайтесь и обсуждайте в выпуске:01 Что такое антивибрация и дросселирование и каковы сценарии их применения

опровергать

Anti-shake, как следует из названия, предотвращает дрожание, чтобы не перепутать одно событие с несколькими, набор текста на клавиатуре — это операция анти-дрожания, с которой вы будете сталкиваться каждый день.

Чтобы понять концепцию, вы должны сначала понять контекст, в котором она применяется. Какие в мире JS есть сцены, предотвращающие тряску?

  1. Кнопки, такие как вход в систему и отправка текстовых сообщений, не позволяют пользователям нажимать слишком быстро, поэтому отправляются несколько запросов, и требуется защита от сотрясений.
  2. При изменении размера окна браузера число изменений слишком частое, что приводит к слишком большому количеству вычислений. В настоящее время оно должно быть на месте один раз, поэтому используется защита от сотрясений.
  3. Текстовый редактор сохраняет в реальном времени и сохраняет через одну секунду без каких-либо изменений

Код выглядит следующим образом, вы можете увидетьAnti-shake фокусируется на очисткеclearTimeout(timer)

function debounce (f, wait) {
  let timer
  return (...args) => {
    clearTimeout(timer)
    timer = setTimeout(() => {
      f(...args)
    }, wait)
  }
}

дроссель

Дросселирование, как следует из названия, контролирует поток воды. Контролируйте частоту событий, например, раз в 1 с или даже раз в минуту. Аналогично пределу скорости, контролируемому сервером и шлюзом.

  1. scrollсобытия, расчет информации о местоположении каждую секунду и т. д.
  2. События воспроизведения браузера, информация о прогрессе рассчитывается каждую секунду и т. д.
  3. Поле ввода выполняет поиск в режиме реального времени и отправляет запрос на отображение выпадающего списка, а также отправляет запрос каждую секунду (также может выполнять анти-встряску)

Код выглядит следующим образом, вы можете увидетьДросселирование фокусируется на блокировкеtimer=timeout

function throttle (f, wait) {
  let timer
  return (...args) => {
    if (timer) { return }
    timer = setTimeout(() => {
      f(...args)
      timer = null
    }, wait)
  }
}

Резюме (краткий ответ)

  • Anti-shake: чтобы предотвратить дрожание, триггер события будет сброшен в течение единицы времени, чтобы избежать многократного запуска события из-за случайной травмы.Реализация кода фокусируется на очисткеclearTimeout. Антишейк можно сравнить с ожиданием лифта, пока входит один человек, ему нужно какое-то время подождать. Бизнес-сценарии имеют дублированные представления, чтобы избежать многократного нажатия кнопки входа.
  • Регулирование: для управления потоком событие может запускаться только один раз в единицу времени, аналогично ограничению скорости на стороне сервера.Реализация кода фокусируется на разблокировке и блокировкеtimer=timeout; timer=null. Дроссельную заслонку можно сравнить со светофором, и у вас может быть партия каждый раз на красный свет.

02 Во фронтенд-разработке, как получить уникальный идентификатор браузера

Подробнее: Как получить уникальный идентификатор браузера, по какому принципу

Общайтесь и обсуждайте в выпуске:02 Во фронтенд-разработке, как получить уникальный идентификатор браузера

Из-за разного рисунка системной видеокартыcanvasАлгоритм, антиалгоритм и т. д., так втянутые в данные изображенияCRCВерификация тоже бывает разной.

function getCanvasFp () {
  const canvas = document.getElementById('canvas')
  const ctx = canvas.getContext('2d')
  ctx.font = '14px Arial'
  ctx.fillStyle = '#ccc'
  ctx.fillText('hello, shanyue', 2, 2)
  return canvas.toDataURL('image/jpeg')
}

Поэтому согласноcanvasМожно получить информацию об отпечатках пальцев браузера.

  1. рисоватьcanvas,Получатьbase64датаурл
  2. Сделайте это в строке dataurlmd5Расчет дайджеста для получения информации об отпечатках пальцев

Тем не менее, есть зрелые решения для общих нужд.Если вы используете его в производственной среде, вы можете использовать следующие библиотеки

Он получает информацию об отпечатках пальцев браузера на основе следующей информации:И эта информация становитсяcomponent

  1. canvas
  2. webgl
  3. UserAgent
  4. AudioContext
  5. Поддержка современных API и др.
requestIdleCallback(function () {
  Fingerprint2.get((components) => {
    const values = components.map((component) => component.value)
    const fp = Fingerprint2.x64hash128(values.join(''), 31)
  })
})

существуетfingerprintjs2в, дляcomponentтакже классифицируется

  • browser independent component:немногоcomponentОдно и то же устройство может получать одинаковое значение в разных браузерах, а некоторые независимые браузеры получают разные значения.
  • stable component: немногоcomponentЗначения изменяются после обновления, известные как нестабильные компоненты.

В реальном бизнесе соответствующие компоненты могут быть выбраны в соответствии с бизнесом.

const options = {
  excludes: {userAgent: true, language: true}
}

краткий ответ

согласно сcanvasИнформация об отпечатках пальцев браузера может быть получена

  1. рисоватьcanvas,Получатьbase64датаурл
  2. Сделайте это в строке dataurlmd5Расчет дайджеста для получения информации об отпечатках пальцев

Для производственного использования вы можете использоватьfingerprintjs2, в соответствии с потребностями бизнеса, например, может ли одно устройство быть кроссбраузерным, выберите соответствующийcomponent

03 Как получить IP клиента в серверном приложении

Общайтесь и обсуждайте в выпуске:03 Как получить IP клиента в серверном приложении

Если естьx-forwarded-forзаголовок запроса, затем берем первый среди них IP, иначе берем remoteAddr сокета, установившего соединение.

а такжеx-forwarded-forПо сути, он стал стандартным HTTP-заголовком на основе прокси. Формат выглядит следующим образом. Видно, что первый IP-адрес представляет его реальный IP-адрес. Вы можете обратиться к MDNX-Forwarded-For

X-Forwarded-For: 203.0.113.195, 70.41.3.18, 150.172.238.178
X-Forwarded-For: <client>, <proxy1>, <proxy2>

Ниже приведеныkoaКак получить ИП

  get ips() {
    const proxy = this.app.proxy;
    const val = this.get(this.app.proxyIpHeader);
    let ips = proxy && val
      ? val.split(/\s*,\s*/)
      : [];
    if (this.app.maxIpsCount > 0) {
      ips = ips.slice(-this.app.maxIpsCount);
    }
    return ips;
  },

  get ip() {
    if (!this[IP]) {
      this[IP] = this.ips[0] || this.socket.remoteAddress || '';
    }
    return this[IP];
  },

См. исходный код:GitHub.com/ смотри, о, да/ смотри, о, нет…

04 Как js заменяет одну подстроку другой подстрокой?

Дополнительное описание: Предположим, есть строка `hello.hello.hello.`, которую нужно заменить на `AAA`, то есть заменить `hello.` на `A`

Общайтесь и обсуждайте в выпуске:04 Как js заменяет одну подстроку другой подстрокой?

Если вам нужно полностью заменить строку, вы можете использоватьString.prototype.replace(re, replacer), где необходимо включить регулярные выраженияglobal flag

const s = 'foo foo foo'
s.replce(/foo/g, 'bar')

Как и в названии,Можно ли использовать регулярные выражения для замены подстрок

отвечать:Нет, потому что могут быть специальные символы при использовании подстрок для построения регулярных выражений, что может вызвать проблемы,следующим образом

// 期待结果: 'AhelloX hello3 '
> 'hello. helloX hello3 '.replace(new RegExp('hello. ', 'g'), 'A')
< "AAA"

пока вjavascriptЕсть только один умный способ заменить подстроки в:str.split('foo').join('bar')

> 'hello. hello. hello. '.split('hello. ').join('A')
< "AAA"

Какой умный (глупый) чудесный (неуклюжий) способ! ! ! ! !Вероятно, TC39 тоже осознал проблему, поэтому вышел новый API,существуетESNextсередина

String.prototype.replaceAll()

'aabbcc'.replaceAll('b', '.'); 
// 'aa..cc'

Подробная документация находится наString.prototype.replaceAll

Резюме (и прямой ответ)

два пути

  • str.split('foo').join('bar')
  • str.replaceAll('foo', 'bar'),существуетESNextСредний, в настоящее время плохо поддерживается

05 Как получить память процесса и следить за ней

Подробнее: При написании скриптов иногда возникает OOM из-за избыточной памяти, так как же узнать память процесса? И как это контролировать

Общайтесь и обсуждайте в выпуске:05 Как получить память процесса и следить за ней

пройти черезpsМожет знать память, занятую процессом

$ ps -O rss -p 3506
  PID   RSS S TTY          TIME COMMAND
 3506  6984 S pts/1    00:00:00 vim

Если вы хотите контролировать память, вы должны использовать команду, которая является всемогущей для процессаpidstat(PS: Как только вы услышите название, вы поймете, для чего оно нужно)

## -r 显示内存信息
## -p 指定 pid
## 1: 每个一秒打印一次
$ pidstat -r -p 3506 1
Linux 3.10.0-957.21.3.el7.x86_64 (shanyue)      11/04/19        _x86_64_        (2 CPU)

20:47:35      UID       PID  minflt/s  majflt/s     VSZ    RSS   %MEM  Command
20:47:36        0      3506      0.00      0.00  139940   6984   0.18  vim
20:47:37        0      3506      0.00      0.00  139940   6984   0.18  vim
20:47:38        0      3506      0.00      0.00  139940   6984   0.18  vim
20:47:39        0      3506      0.00      0.00  139940   6984   0.18  vim
20:47:40        0      3506      0.00      0.00  139940   6984   0.18  vim
20:47:41        0      3506      0.00      0.00  139940   6984   0.18  vim

pidstatпринадлежатьsysstatинструменты производительности Linux под , но в Mac, как найти изменения памяти? Можно использовать универсальныйtop/htop

$ htop -p 31796

image

Суммировать

В двух словах, есть следующие три команды

  1. pidstat -r
  2. htop/top -p
  3. ps -O rss -p

Подробнее о мониторинге метрик читайте в моей статье:Заметки о различных индикаторах мониторинга Linux

06 CORS Что делать, если вам нужно указать несколько доменных имен

Общайтесь и обсуждайте в выпуске:06 CORS Что делать, если вам нужно указать несколько доменных имен

CORSконтролируяAccess-Control-Allow-OriginКонтролируйте, какие доменные имена могут совместно использовать ресурсы, значения следующие

Access-Control-Allow-Origin: <origin> | *

в*представляет все доменные имена,originОт имени указания конкретного доменного имени, как настроить несколько доменных имен?

На данный момент это должно быть реализовано через код,Согласно заголовку запросаOriginустановить заголовок ответаAccess-Control-Allow-Origin, то что такое происхождение?

Заголовок запроса: Происхождение

Не все запросы автоматически принесутOrigin, в браузере сOriginЛогика следующая

  1. Если есть перекрестный домен, принеситеOrigin, значение — текущее доменное имя
  2. Если нет перекрестного домена, безOrigin

После того, как логика понятна, оAccess-Control-Allow-OriginЛогика настройки нескольких доменных имен также очень понятна.

  1. Если в заголовке запроса нетOrigin, если окажется, что это не междоменный, обработка не будет выполняться
  2. Если в заголовке запроса естьOrigin, проверенный междоменный, согласноOriginустановить соответствующийAccess-Control-Allow-Origin: <Origin>

Это делается с использованием псевдокода следующим образом:

// 获取 Origin 请求头
const requestOrigin = ctx.get('Origin');

// 如果没有,则跳过
if (!requestOrigin) {
  return await next();
}

// 设置响应头
ctx.set('Access-Control-Allow-Origin', requestOrigin)

Vary: Origin

В настоящее время CORS можно контролировать для нескольких доменных имен, но в настоящее время предполагается, что есть два доменных имени, обращающихся кstatic.shanyue.techкросс-происхождения ресурсов

  1. foo.shanyue.tech, возвращается в заголовке ответаAccess-Control-Allow-Origin: foo.shanyue.tech
  2. bar.shanyue.tech, возвращается в заголовке ответаAccess-Control-Allow-Origin: bar.shanyue.tech

Вроде все работает, а если посередине кеш?

  1. foo.shanyue.tech, возвращается в заголовке ответаAccess-Control-Allow-Origin: foo.shanyue.tech, кэшируется CDN
  2. bar.shanyue.tech, из-за кэширования, возвращается в заголовке ответаAccess-Control-Allow-Origin: foo.shanyue.tech, возникает междоменная проблема

В настоящее время,Vary: Originна сцене, представляющие разныеOriginКешировать разные ресурсы

Резюме (краткий ответ)

Как CORS указывает несколько доменных имен?

Согласно заголовку запросаOriginустановить заголовок ответаAccess-Control-Allow-Origin, идея такая

  1. всегда устанавливайтеVary: Origin, чтобы избежать повреждения конфигурации CORS кешем CDN.
  2. Если в заголовке запроса нетOrigin, если окажется, что это не междоменный, обработка не будет выполняться
  3. Если в заголовке запроса естьOriginПодтвердите междоменный доступ браузера, согласноOriginустановить соответствующийAccess-Control-Allow-Origin: <Origin>

Используйте псевдокод для реализации следующим образом

// 获取 Origin 请求头
const requestOrigin = ctx.get('Origin');

ctx.set('Vary', 'Origin')

// 如果没有,则跳过
if (!requestOrigin) {
  return await next();
}

// 设置响应头
ctx.set('Access-Control-Allow-Origin', requestOrigin)

Связанные вопросы:Как избежать кэширования мобильных страниц CDN для ПК

07 Поскольку конфигурация cors может использоваться для междоменного контроля, может ли она предотвратить атаки CSRF?

Общайтесь и обсуждайте в выпуске:07 Поскольку конфигурация cors может использоваться для междоменного контроля, может ли она предотвратить атаки CSRF?

Не работает вообще для CORS

  1. formОтправить не удалосьCORSобнаружить, вы можете протестировать локально
  2. несмотря наxhrа такжеfetchКоммит заблокирован CORS,Но для простых запросов запрос все равно отправляется, спровоцировал нападение

08 Как избежать кэширования мобильных страниц CDN для ПК

Общайтесь и обсуждайте в выпуске:08 Как избежать кэширования мобильных страниц CDN для ПК

Если сторона ПК и мобильная сторона представляют собой набор кодов, этой проблемы не возникнет.Эта проблема возникает на стороне ПК и на стороне мобильного устройства, которые представляют собой два набора кодов, но имеют общее доменное имя.

использоватьnginxКонфигурация выглядит следующим образом, согласно UA, чтобы определить, является ли мобильный терминал, и использовать другую логику (судя, подвержен ли мобильный терминал UA проблемам)

location / {
    // 默认 PC 端
    root /usr/local/website/web;
    
    # 判断 UA,访问移动端
    if ( $http_user_agent ~* "(Android|webOS|iPhone|iPad|BlackBerry)" ){ 
        root /usr/local/website/mobile;
    }
 
    index index.html index.htm;
}

Решение обычно заключается в использованииVaryЗаголовки ответов для управления кэшированием CDN различных заголовков запросов.

можно использовать здесьVary: User-Agent, что означает, что если User-Agent отличается, повторно инициируйте запрос вместо чтения страницы из кеша.

Vary: User-Agent

Конечно,User-AgentЭто слишком много, и инвалидация кеша будет слишком большой в это время.

краткий ответ

использоватьVary: User-Agent, кэшируется согласно UA.

Vary: User-Agent

Но лучше, чтобы такой ситуации не было.При наличии двух наборов кодов на ПК и мобильных терминалах рекомендуется использовать два доменных имени по следующим причинам.

  1. nginxОпределение склонности мобильных терминалов к ошибкам
  2. Не поддерживает кеширование

09 Как реализовать стиль таблицы с однорядной и двухрядной полосой

Общайтесь и обсуждайте в выпуске:09 Как реализовать стиль таблицы с однорядной и двухрядной полосой

пройти черезcss3средний класс:nth-childреализовать. в:nth-child(an+b)соответствие нижнему индексу{ an + b; n = 0, 1, 2, ...}и результат является подэлементом целого числа

  • nth-child(2n)/nth-child(even): стиль двойной линии
  • nth-child(2n+1)/nth-child(odd): стиль одной линии

вtrПредставляя строки в таблице, очень просто внедрить одно- и двойные стили строки в таблице:

tr:nth-child(2n) {
  background-color: red;
}


tr:nth-child(2n+1) {
  background-color: blue;
}

По аналогии:

  1. Как сопоставить первые три дочерних элемента::nth-child(-n+3)
  2. Как сопоставить последние три дочерних элемента::nth-last-child(-n+3)

10 Кратко опишите специфику css

Общайтесь и обсуждайте в выпуске:10 Кратко опишите специфику css

css specificityТо есть вес селектора в css, по очереди спускаются следующие три типа селекторов

  1. idселектор, например#app
  2. class,attributeа такжеpseudo-classesселектор, например.header,[type="radio"]а также:hover
  3. typeСелекторы тегов и селекторы псевдоэлементов, такие какh1,pа также::before

где селектор подстановочных знаков*, комбинированный селектор+ ~ >, отрицая селектор псевдокласса:not()Не влияет на приоритет

Встроенные стили<div class="foo" style="color: red;"></div>а также!important(самый высокий) имеет больший вес

:notПриоритетное влияние - codepenКак можно заметить:notНе влияет на приоритет селектора

11 В чем разница между module.exports и экспортом в node

Общайтесь и обсуждайте в выпуске:11 В чем разница между module.exports и экспортом в node

В одном предложении:exportsдаmodule.exportsцитаты, еслиexportsБез переназначения нет никакой разницы между двумя

похоже на следующее

const exports = module.exports

Как будут получены следующие результаты?

module.exports = 100
exports = 3

в конце концов, очевидно, экспортировал бы 100exportsпереназначен.

Как это реализовано в исходном коде узла?Это видно из исходного кодаexportsсуть

module wrapper

Подробности смотрите в исходном коде:GitHub.com/node будет /node…, видно, что гипотеза

Как мы все знаем, весь код модуля в узле заключен в эту функцию.

(function(exports, require, module, __filename, __dirname) {
  exports.a = 3
});

Хотя в следующем исходном коде указано,exportsКак ты получил это

const dirname = path.dirname(filename);
const require = makeRequireFunction(this, redirects);
let result;
// 从这里可以看出来 exports 的实质
const exports = this.exports;
const thisValue = exports;
const module = this;
if (requireDepth === 0) statCache = new Map();
if (inspectorWrapper) {
  result = inspectorWrapper(compiledWrapper, thisValue, exports,
                            require, module, filename, dirname);
} else {

  // 这里是模块包装函数
  result = compiledWrapper.call(thisValue, exports, require, module,
                                filename, dirname);
}

12 Как получить количество онлайн-пользователей в текущей системе (количество одновременных пользователей)

Подробное описание: Некоторые системы SaaS ограничивают количество команд и количество одновременных онлайн-подключений из соображений ценообразования Как этого добиться?

Общайтесь и обсуждайте в выпуске:12 Как получить количество онлайн-пользователей в текущей системе (количество одновременных пользователей)

Некоторые SaaS-системы ограничивают количество команд и количество одновременных онлайн-подключений исходя из соображений ценовой политики.Как этого добиться?

пройти черезredisизzsetКоличество одновременных пользователей может быть достигнуто.

Когда пользователь запрашивает любой интерфейс, реализуйте промежуточное ПО и обрабатывайте следующую логику.

// 当一个用户访问任何接口时,对该用户Id,写入 zset
await redis.zadd(`Organization:${organizationId}:concurrent`, Date.now(), `User:${userId}`)

// 查询当前机构的并发数
// 通过查询一分钟内的活跃用户来确认并发数,如果超过则抛出特定异常
const activeUsers = await redis.zrangebyscore(`Organization:${organizationId}:concurrent`, Date.now() - 1000 * 60, Date.now())

// 查出并发数
const count = activeUsers.length

// 删掉过期的用户
await redis.zrembyscore(`Organization:${organizationId}:concurrent`, Date.now() - 1000 * 60, Date.now())

Суммировать

  1. Всякий раз, когда пользователь обращается к сервису, записывайте идентификатор пользователя в приоритетную очередь с весом текущего времени.
  2. Рассчитать количество пользователей заведения за одну минуту исходя из веса (т.е. времени)
  3. Удалить пользователей, срок действия которых истек более одной минуты

13 Как конвертировать данные JSON в Demo.json и загрузите файл

Общайтесь и обсуждайте в выпуске:13 Как преобразовать данные json в demo.json и скачать файл

json рассматривается как строка, которую можно использоватьDataURLскачивать

Text -> DataURL

Помимо использования DataURL, его также можно преобразовать в URL-адрес объекта для загрузки.

Text -> Blob -> Object URL

Вы можете вставить следующий код прямо в консоль, чтобы загрузить файл

function download (url, name) {
  const a = document.createElement('a')
  a.download = name
  a.rel = 'noopener'
  a.href = url
  // 触发模拟点击
  a.dispatchEvent(new MouseEvent('click'))
  // 或者 a.click()
}

const json = {
  a: 3,
  b: 4,
  c: 5
}
const str = JSON.stringify(json, null, 2)

// 方案一:Text -> DataURL
const dataUrl = `data:,${str}`
download(dataUrl, 'demo.json')

// 方案二:Text -> Blob -> ObjectURL
const url = URL.createObjectURL(new Blob(str.split('')))
download(url, 'demo1.json')

Суммировать

  1. Чтобы имитировать загрузку, вы можете создать новую,<a href="url" download><a>маркировать и устанавливатьurlа такжеdownloadсвойства для загрузки
  2. можно сделать, поставивjsonпревратиться вdataurlдля создания URL
  3. можно сделать, поставивjsonПеревести вBlobконвертировано вObjectURLдля создания URL

14 Как следить за содержимым буфера обмена в браузере

Общайтесь и обсуждайте в выпуске:14 Как следить за содержимым буфера обмена в браузере

пройти черезClipboard APIВы можете получить содержимое буфера обмена, но вам нужно получитьclipboard-read, вот код для чтения содержимого буфера обмена:

// 是否能够有读取剪贴板的权限
// result.state == "granted" || result.state == "prompt"
const result = await navigator.permissions.query({ name: "clipboard-read" })

// 获取剪贴板内容
const text = await navigator.clipboard.readText()

Примечание. Этот метод находится вdevtoolsне действует в

Связанные вопросы:【Q019】Как реализовать функцию выбранной копии

Подписывайтесь на меня

Я Шаньюэ, и я работаю надОтветьте на большой заводской высокочастотный вопрос интервью короткими ответами, которые можно прочитать за пять минут каждый день..

Добро пожаловать, чтобы обратить внимание на публичный аккаунт [Internet Big Factory Recruitment], регулярно размещайте внутреннюю информацию о большой фабрике и краткие ответы на вопросы интервью, учитесь по пять минут в день и входите в большую фабрику в течение полугода.

每天五分钟,半年大厂中