Передовые вопросы для собеседования @JS статьи

интервью
Передовые вопросы для собеседования @JS статьи

содержание

JS

Es6

Node

оптимизация производительности

интернет браузер

алгоритм

Поговорим о лексической области видимости в js

В js есть только лексическая область видимости, что означает, что область видимости определяется во время определения, а не во время выполнения. Например:

var value = 1;
 
function foo() {
    console.log(value);
}
 
function bar() {
    var value = 2;
    foo();
}
 
bar();<br>//1

Примечание. with и eval могут изменять лексическую область видимости.

что такое закрытие

Определение замыкания в «nodejs простыми словами»:

В js метод, который реализует внешнюю область для доступа к переменным во внутренней области, называется «замыканием».

Разговор о сборке мусора js (GC)

Стратегия сборки мусора v8 в основном основана на механизме сборки мусора поколений. Память делится на новое поколение и старое поколение, соответственно используются разные алгоритмы.

Новое поколение использует алгоритм Scavenge

Алгоритм, используемый Scavenge для нового поколения, представляет собой алгоритм сборки мусора, реализованный посредством репликации. Он делит память на два пространства: от и до. Каждый раз, когда выполняется gc, уцелевшие объекты из пространства from копируются в пространство to. Затем роли двух пространств меняются местами (т. е. меняются местами).
Этот алгоритм жертвует пространством ради времени, поэтому он подходит для нового поколения, потому что его жизненный цикл объекта короткий.

Старое поколение принимает Mark-Sweep и Mark-Compact

Объекты в старом поколении имеют большое время выживания, что не подходит для алгоритма Scavenge.
Mark-Sweep означает Mark Sweep. Scavenge копирует только уцелевшие объекты, а Mark-Sweep удаляет только мертвые объекты. Алгоритм разбит на два шага:

  1. Перебирать все объекты в куче и отмечать живые объекты
  2. Очистить неотмеченные объекты

Есть проблема с Mark-Sweep.После очистки мертвого объекта пространство памяти будет прерывистым.Если в это время будет выделен другой большой объект, все фрагменты пространства не смогут завершить выделение, что приведет к срабатыванию gc в продвигать. В настоящее время v8 будет использовать алгоритм Mark-Compact.
Mark-Copact означает отделку маркировкой. Он переместит живой объект в один конец после завершения маркировки и непосредственно очистит память за границей после завершения движения. Из-за процесса сортировки он медленнее, чем Mark-Sweep, который в основном используется в node.

Incremental Marking

Чтобы избежать несоответствий между логикой приложения Javascript и тем, что видит сборщик мусора, логика приложения останавливается во время сборки мусора. Такое поведение называется остановкой мира. Это оказывает большее влияние на старшее поколение.
Incremental Marking называется инкрементной разметкой, то есть она делится на множество мелких «шагов».После выполнения каждого «шага» некоторое время выполняется Javascript, поочередно выполняются сборка мусора и логика приложения.
После использования добавочной маркировки максимальное время паузы gc уменьшается примерно до 1/6 от исходного.

лимит памяти v8

  • Максимум 64-битной системы составляет около 1,4 ГБ.
  • Максимум 32-битной системы составляет около 0,7 ГБ.

Просмотр использования памяти в узле

➜  ~ node
> process.memoryUsage()   //node进程内存使用
{ rss: 27054080,   // 进程常驻内存
  heapTotal: 7684096,  // 已申请到的堆内存
  heapUsed: 4850344,    // 当前使用的堆内存
  external: 9978  // 堆外内存(不是通过v8分配的内存)
  
> os.totalmem()  //系统总内存
17179869184

> os.freemem()  //系统闲置内存
3239858176

Расскажите мне о шаблонах проектирования, которые вы знаете

модель публикации-подписки

В js модель событий эквивалентна традиционной модели публикации-подписки.Реализовать EventEmiter в узле

режим стратегии

Определение: определите серию алгоритмов, инкапсулируйте их один за другим и сделайте их взаимозаменяемыми.

Шаблон стратегии для реализации проверки формы
const strategies = {
    isNoEmpty: function(value, errorMsg){
        if(value.trim() === ''){
            return errorMsg
        }
    },
    maxLength: function(value, errorMsg, len) {
        if(value.trim() > len) {
            return errorMsg
        }
    }
}

class Validator {
  constructor() {
    this.catch = [];
  }
  add(value, rule, errorMsg, ...others) {
    this.catch.push(function() {
      return strategies[rule].apply(this, [value, errorMsg, ...others]);
    });
  }
  start() {
    for (let i = 0, validatorFunc; (validatorFunc = this.catch[i++]); ) {
      let msg = validatorFunc();
      if (msg) {
        return msg;
      }
    }
  }
}

//使用
const validatorFunc = function() {
    const validator = new Validator();
    validator.add(username, 'isNoEmpty', '用户名不能为空');
    validator.add(password, 'isNoEmpty', '密码不能为空');
    const USERNAME_LEN = PASSWORD_LEN = 10;
    validator.add(username, 'maxLength', `用户名不能超过${USERNAME_LEN}个字`, USERNAME_LEN);
    validator.add(password, 'isNoEmpty', `密码不能为空${PASSWORD_LEN}个字`, PASSWORD_LEN);
    let msg = validator.start();
    if(msg) {
        return msg;
    }
}

командный режим

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

Анимация в командном режиме
class MoveCommand {
  constructor(reciever, pos) {
    this.reciever = reciever;
    this.pos = pos;
    this.oldPos = null;
  }
  excute() {
    this.reciever.start("left", this.pos, 1000);
    this.reciever.getPos();
  }
  undo() {
    this.reciever.start("left", this.oldPos, 1000);
  }
}


Различия между модулями ES6 и модулями CommonJS

  1. CommonJS выводит копию значения, а модули ES6 выводят ссылку на значение.
    То есть после того, как ссылка CommonJS изменит значение переменной в модуле, другие модули, на которые ссылаются, не изменятся, но модули ES6 изменятся.

  2. CommonJS загружается во время выполнения, модули ES6 являются выходными интерфейсами во время компиляции.
    Причина, по которой Tree Shaking в Webpack основана на ES6, заключается в том, что ES6 может определять зависимости во время компиляции. Поскольку использование предустановки babel-preset-2015 по умолчанию компилирует модули ES6 в CommonJS, вам необходимо вручную изменить эту предустановку, если вы хотите использовать Tree Shaking.

module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: [['babel-preset-es2015', {modules: false}]],
          }
        }
      }
    ]
}

Принцип реализации асинхронной функции

Асинхронная функция основана на реализации генератора, поэтому она требует знаний, связанных с генератором. Прежде чем не будет асинхронной функции, библиотека co обычно используется для выполнения генератора, поэтому мы также можем смоделировать реализацию async через co.

function Asyncfn() {
  return co(function*() {
    //.....
  });
}
function co(gen) {
  return new Promise((resolve, reject) => {
    const fn = gen();
    function next(data) {
      let { value, done } = fn.next(data);
      if (done) return resolve(value);
      Promise.resolve(value).then(res => {
        next(res);
      }, reject);
    }
    next();
  });
}

Расскажите о цикле событий (EventLoop) в браузере и узле.

браузер

Как показано на рисунке: Браузер относительно прост. Есть две очереди событий. Когда основной поток простаивает, очередь микрозадач будет очищена, а функции обратного вызова в очереди задач (очередь задач макросов) будут выполняться по очереди. .После каждого выполнения Микрозадача будет очищаться.

«Текущий стек выполнения» -> «микрозадача» -> «обратный вызов из очереди задач» -> «микрозадача» -> ... (непрерывное потребление очереди задач) -> «микрозадача»

nodejs

Существуют некоторые различия между механизмом в узле и браузере. Очередь задач в узле разбита на несколько этапов, микрозадача очищается после стадии (в браузере, после завершения каждой задачи), этапы следующие:

   ┌───────────────────────┐
┌─>│        timers         │<————— 执行 setTimeout()、setInterval() 的回调
│  └──────────┬────────────┘
|             |<-- 执行所有 Next Tick Queue 以及 MicroTask Queue 的回调
│  ┌──────────┴────────────┐
│  │     pending callbacks │<————— 执行由上一个 Tick 延迟下来的 I/O 回调(待完善,可忽略)
│  └──────────┬────────────┘
|             |<-- 执行所有 Next Tick Queue 以及 MicroTask Queue 的回调
│  ┌──────────┴────────────┐
│  │     idle, prepare     │<————— 内部调用(可忽略)
│  └──────────┬────────────┘     
|             |<-- 执行所有 Next Tick Queue 以及 MicroTask Queue 的回调
|             |                   ┌───────────────┐
│  ┌──────────┴────────────┐      │   incoming:   │ - (执行几乎所有的回调,除了 close callbacks 以及 timers 调度的回调和 setImmediate() 调度的回调,在恰当的时机将会阻塞在此阶段)
│  │         poll          │<─────┤  connections, │ 
│  └──────────┬────────────┘      │   data, etc.  │ 
│             |                   |               | 
|             |                   └───────────────┘
|             |<-- 执行所有 Next Tick Queue 以及 MicroTask Queue 的回调
|  ┌──────────┴────────────┐      
│  │        check          │<————— setImmediate() 的回调将会在这个阶段执行
│  └──────────┬────────────┘
|             |<-- 执行所有 Next Tick Queue 以及 MicroTask Queue 的回调
│  ┌──────────┴────────────┐
└──┤    close callbacks    │<————— socket.on('close', ...)
   └───────────────────────┘

Здесь мы в основном фокусируемся на 3 этапах: таймер, опрос и проверка, среди которых очередь опроса является относительно сложной:

Этап опроса выполняет две важные функции:
1. Рассчитайте, как долго он должен блокировать и опрашивать ввод-вывод.
2. Затем обработайте события в очереди опроса.

Когда цикл обработки событий входит в фазу опроса, а таймер не запланирован, произойдет одно из двух:
1. Если очередь опроса не пуста, цикл обработки событий будет циклически проходить через очередь обратного вызова и выполнять их синхронно до тех пор, пока очередь не будет исчерпана или не будет достигнут жесткий предел, зависящий от системы.
2. Если очередь опроса пуста, происходят еще две вещи:
А. Если сценарии были запланированы с помощью setImmediate(), цикл событий завершит фазу опроса и продолжит фазу проверки для выполнения этих запланированных сценариев.
б) Если сценарий не был запланирован с помощью setImmediate(), цикл обработки событий будет ожидать добавления обратного вызова в очередь, а затем немедленно его выполнить.

Как только очередь опроса станет пустой, цикл обработки событий проверит наличие таймера, достигшего порогового значения времени. Если один или несколько таймеров готовы, цикл обработки событий повторяет фазу таймера, чтобы выполнить обратные вызовы для этих таймеров.

Пожалуйста, обратитесь кThe Node.js Event Loop, Timers, and process.nextTick()
Китайский язык:Цикл событий Node.js, таймеры и process.nextTick()

Программно понимать различия в браузерах и узлах

setTimeout(() => {
  console.log("timer1");
  Promise.resolve().then(function() {
    console.log("promise1");
  });
}, 0);

setTimeout(() => {
  console.log("timer2");
  Promise.resolve().then(function() {
    console.log("promise2");
  });
}, 0);

Порядок в браузере: таймер1 -> обещание1 -> таймер2 -> обещание2.
Порядок в узле: таймер1 -> таймер2 -> обещание1 -> обещание2.
Эта тема является хорошей иллюстрацией того, что микрозадача в узле очищается после выполнения задачи этапа.

Реализовать EventEmiter в узле

Простая реализация:

class EventsEmiter {
  constructor() {
    this.events = {};
  }
  on(type, fn) {
    const events = this.events;
    if (!events[type]) {
      events[type] = [fn];
    } else {
      events[type].push(fn);
    }
  }
  emit(type, ...res) {
    const events = this.events;
    if (events[type]) {
      events[type].forEach(fn => fn.apply(this, res));
    }
  }
  remove(type, fn) {
    const events = this.events;
    if (events[type]) {
      events[type] = events[type].filer(lisener => lisener !== fn);
    }
  }
}

Реализовать метод обещания модуля util в узле

let fs = require("fs");
let read = fs.readFile;

function promisify(fn) {
  return function(...args) {
    return new Promise((resolve, reject) => {
      fn(...args, (err, data) => {
        if (err) {
          reject(err);
        }
        resolve(data);
      });
    });
  };
}

// 回调用法
// read("./test.json", (err, data) => {
//   if (err) {
//     console.error("err", err);
//   }
//   console.log("data", data.toString());
// });

// promise用法
let readPromise = promisify(read);

readPromise("./test.json").then(res => {
  console.log("data", res.toString());
});

Как реализовать пользовательский поток

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

Пример своего рода метод, который необходимо реализовать
поток только для чтения Readable _read
поток только для записи Writable _write, _writev, _final
поток чтения и записи Duplex _read, _write, _writev, _final
работать с записанными данными, затем читать результат Transform _transform, _flush, _final

Возьмем дуплексный поток в качестве примера:

const { Duplex } = require('stream');

class Myduplex extends  Duplex {
  constructor(arr, opt) {
    super(opt);
    this.arr = arr
    this.index = 0
  }
  //实现可读流部分
  _read(size) {
    this.index++
    if(this.index === 3) {
        this.push(null) 
    } else {
        this.push(this.index.toString())
    }
  }
  //实现可写流
  _write(chunk, encoding, callback) {
    this.arr.push(chunk.toString())
    callback()
  }
}

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

Оптимизация производительности dns-prefetch, prefetch, preload, defer, async

dns-prefetch

Преобразование доменного имени в IP — это трудоемкий процесс, и dns-prefetch позволяет браузеру сделать это за вас, когда он простаивает. Особенно большие веб-сайты будут использовать несколько доменных имен, и предварительная выборка DNS в настоящее время еще более необходима.

//来自百度首页
<link rel="dns-prefetch" href="//m.baidu.com">

prefetch

Предварительная выборка обычно используется для предварительной загрузки ресурсов, которые могут быть использованы. Как правило, это оценка поведения пользователя. Браузер загружает предварительно загруженные ресурсы, когда он бездействует.

<link rel="prefetch" href="http://www.example.com/">

preload

В отличие от предварительной выборки, префект обычно загружает ресурсы страницы, которые могут быть использованы в дальнейшем, в то время как предварительная загрузка предназначена для загрузки таких ресурсов, как сценарии, стили, шрифты, изображения и т. д., которые будут использоваться на текущей странице. Таким образом, предварительная загрузка не загружается во время простоя, она имеет более высокий приоритет и будет занимать количество http-запросов.

<link rel='preload' href='style.css' as="style" onload="console.log('style loaded')"

как значения включают

  • "script"
  • "style"
  • "image"
  • "media"
  • "документ" Метод onload — это функция обратного вызова для завершения загрузки ресурса.

отложить и асинхронно

//defer
<script defer src="script.js"></script>
//async
<script async src="script.js"></script>

И defer, и async — это асинхронная (параллельная) загрузка ресурсов, разница в том, что async выполняется сразу после загрузки, а defer не выполняется после загрузки, а выполняется после разбора всех элементов, то есть до срабатывания события DOMContentLoaded.
Поскольку ресурсы, загруженные асинхронным режимом, загружаются и выполняются, он не может гарантировать порядок, и отложенные сценарии будут выполняться по порядку.

Разговор об оптимизации производительности реагирования

shouldComponentUpdate

Пример: Ниже приведена обработка компонента внутренней маски в модальном компоненте antd-design-mobile.

import * as React from "react";

export interface lazyRenderProps {
  style: {};
  visible?: boolean;
  className?: string;
}

export default class LazyRender extends React.Component<lazyRenderProps, any> {
  shouldComponentUpdate(nextProps: lazyRenderProps) {
    return !!nextProps.visible;
  }
  render() {
    const props: any = { ...this.props };
    delete props.visible;
    return <div {...props} />;
  }
}

immutable

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

  • лучшая производительность
  • безопаснее Неизменные недостатки:
  • Библиотека относительно большая (около 16k после сжатия)
  • апи и js несовместимы

Решение: бесшовное неизменное Бесшовно-неизменяемая библиотека не полностью реализует постоянную структуру данных, но использует Object.defineProperty для расширения объектов JS Object и Array, поэтому она поддерживает тот же API, а в библиотеке меньше кода, примерно на 2 КБ после сжатия.

оптимизация на основе ключей

В документации подчеркивается, что ключ должен быть гарантированно уникальным в текущей области и не использовать индекс текущего цикла (особенно в длинных списках).
Ссылаться нареагировать JS.org/docs/recon от…

Расскажите о процессе рендеринга в браузере

Основной процесс браузера: Браузерный процесс

  1. Отвечает за загрузку ресурсов
  2. Создать и уничтожить процесс визуализации
  3. Отвечает за рендеринг растрового изображения, сгенерированного процессом рендеринга, на страницу.
  4. взаимодействовать с пользователями

Ядро браузера: процесс рендеринга

поток движка js

Состоит из основного потока и нескольких потоков веб-воркеров, рабочие веб-потоки не могут работать в домашних условиях.

Графический поток

Он используется для анализа html для создания дерева DOM, анализа css для создания CSSOM, макета макета и рисования краски. Перекомпоновка и перерисовка зависят от этой темы

поток событий

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

Поток триггера по времени

setTimeout и setInterval синхронизируются потоком, отсчет времени завершается, и функция обратного вызова помещается в очередь задач.

цепочка HTTP-запросов

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

поток опроса очереди задач

Используется для опроса очереди задач прослушивания

Процесс

  1. получить html-файл
  2. Парсить html сверху вниз
  3. Параллельно запрашивать ресурсы (ресурсы css не будут блокировать парсинг html, но будут блокировать отрисовку страницы. Ресурсы js организуют парсинг html)
  4. Создание дерева DOM и правил стиля
  5. построить дерево визуализации
  6. Выполните процесс компоновки (макет, также известный как перекомпоновка), чтобы определить конкретные координаты элемента на экране.
  7. Рисовать на экране (рисовать)

событие

DOMContentLoaded

Событие DOMContentLoaded запускается, когда исходный HTML-документ полностью загружен и проанализирован (скрипт выполняется, а таблица стилей перед скриптом, которому он принадлежит, загружается и анализируется), запускается событие DOMContentLoaded

onload

Когда все ресурсы загружены, запускается событие onload окна.

Обратитесь к блок-схеме:Woohoo.процесс on.com/view/5ah6861…

Разговор о http2.0

http2.0 — это обновленная версия протокола SPDY. По сравнению с http1.0 он в основном имеет следующие особенности:

  • Бинарное кадрирование
  • сжатие заголовка
  • мультиплексирование
  • приоритет запроса
  • пуш сервера

Для получения подробной информации см.:HTTP----HTTP2.0 новые функции

Реализовать метод сокращения

Обратите внимание на граничные условия: 1. Когда длина массива равна 0, а в начальных параметрах не проходит редукция, выдается ошибка. 2. Reduce имеет возвращаемое значение.

Array.prototype.myReduce = function(fn, initial) {
  if (this.length === 0 && !initial) {
    throw new Error("no initial and array is empty");
  }
  let start = 1;
  let pre = this[0];
  if (initial) {
    start = 0;
    pre = initial;
  }
  for (let i = start; i < this.length; i++) {
    let current = this[i];
    pre = fn.call(this, pre, current, i);
  }
  return pre;
};

Реализуйте метод promise.all, который требует хранения ошибок и параллелизма 3

Стандартный метод all заключается в том, чтобы немедленно установить промис на сбой при обнаружении ошибки и вызвать обратный вызов ошибки. Определение оставшейся ошибки: обещание обнаруживает ошибку и сохраняет ее в возвращаемом результате.

function promiseall(promises) {
  return new Promise(resolve => {
    let result = [];
    let flag = 0;
    let taskQueue = promises.slice(0, 3); //任务队列,初始为最大并发数3
    let others = promises.slice(3); //排队的任务

    taskQueue.forEach((promise, i) => {
      singleTaskRun(promise, i);
    });

    let i = 3; //新的任务从索引3开始
    function next() {
      if (others.length === 0) {
        return;
      }
      const newTask = others.shift();
      singleTaskRun(newTask, i++);
    }

    function singleTaskRun(promise, i) {
      promise
        .then(res => {
          check();
          result[i] = res;
          next();
        })
        .catch(err => {
          check();
          result[i] = err;
          next();
        });
    }
    function check() {
      flag++;
      if (flag === promises.length) {
        resolve(result);
      }
    }
  });
}

Тестовый код:

let p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("1");
  }, 1000);
});
let p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("2");
  }, 1500);
});
let p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("3");
  }, 2000);
});
let p4 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("4");
  }, 2500);
});
let p_e = new Promise((resolve, reject) => {
  // throw new Error("出错");
  reject("错误");
});
let p5 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("5");
  }, 5000);
});

let all = promiseall([p_e, p1, p3, p2, p4, p5]);
all.then(
  data => {
    console.log("data", data);    // [ '错误', '1', '3', '2', '4', '5' ]
  }
);

Найдите высоту бинарного дерева без рекурсивной функции

Давайте сначала посмотрим на реализацию рекурсии (обход бинарного дерева в глубину):

function getBinaryTreeHeigth(node) {
  let maxDeep = 0;
  function next(n, deep) {
    deep++;
    if (n.l) {
      let newDeep = next(n.l, deep);
      if (newDeep > maxDeep) {
        maxDeep = newDeep;
      }
    }
    if (n.r) {
      let newDeep = next(n.r, deep);
      if (newDeep > maxDeep) {
        maxDeep = newDeep;
      }
    }
    return deep;
  }
  next(node, 0);
  return maxDeep;
}

function Node(v, l, r) {
  this.v = v;
  this.l = l;
  this.r = r;
}

Нерекурсивная реализация (обход двоичного дерева в ширину):

function getBinaryTreeHeigth(node) {
  if (!node) {
    return 0;
  }
  const queue = [node];
  let deep = 0;
  while (queue.length) {
    deep++;
    for (let i = 0; i < queue.length; i++) {
      const cur = queue.pop();
      if (cur.l) {
        queue.unshift(cur.l);
      }
      if (cur.r) {
        queue.unshift(cur.r);
      }
    }
  }
  return deep;
}

function Node(v, l, r) {
  this.v = v;
  this.l = l;
  this.r = r;
}

Добавьте два больших числа в js

Даны два неотрицательных целых числа num1 и num2, представленные в виде строк, вернуть их сумму, по-прежнему представленную в виде строк.

Ввод: число1 = '1234', число2 = '987'
Выход: «2221»

function bigIntAdd(str1, str2) {
  let result = [];
  let ary1 = str1.split("");
  let ary2 = str2.split("");
  let flag = false; //是否进位
  while (ary1.length || ary2.length) {
    let result_c = sigle_pos_add(ary1.pop(), ary2.pop());
    if (flag) {
      result_c = result_c + 1;
    }
    result.unshift(result_c % 10);

    if (result_c >= 10) {
      flag = true;
    } else {
      flag = false;
    }
  }
  if(flag) {
    result.unshift('1');
  }
  return result.join("");
}

function sigle_pos_add(str1_c, str2_c) {
  let l = (r = 0);
  if (str1_c) {
    l = Number(str1_c);
  }
  if (str2_c) {
    r = Number(str2_c);
  }
  return l + r;
}

Тестовый код:

const str1 = "1234";
const str2 = "987654321";
const str3 = "4566786445677555";
const str4 = "987";

console.log(bigIntAdd(str1, str4))  //'2221'
console.log(bigIntAdd(str2, str3))  //'4566787433331876'

Реализовать алгоритм случайного перемешивания массива

function disOrder(ary) {
  for (let i = 0; i < ary.length; i++) {
    let randomIndex = Math.floor(Math.random() * ary.length);
    swap(ary, i, randomIndex);
  }
}

function swap(ary, a, b) {
  let temp = ary[a];
  ary[a] = ary[b];
  ary[b] = temp;
}

let ary = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
disOrder(ary);

console.log(ary);

Добавьте «запятую» для разделения чисел

Ввод: '"123456789,012"' Вывод: 123 456 789,012

Обычное решение:

function parseNumber(num) {
  if (!num) return "";
  return num.replace(/(\d)(?=(\d{3})+(\.|$))/g, "$1,");
}

Нерегулярный:

function formatNumber(num) {
  if (!num) return "";
  let [int, float] = num.split(".");
  let intArr = int.split("");
  let result = [];
  let i = 0;
  while (intArr.length) {
    if (i !== 0 && i % 3 === 0) {
      result.unshift(intArr.pop() + ",");
    } else {
      result.unshift(intArr.pop());
    }
    i++;
  }

  return result.join("") + "." + (float ? float : "");
}