Ответы пользователя по тегу JavaScript
  • Как получить индекс объекта в localStorage?

    bingo347
    @bingo347 Куратор тега JavaScript
    Crazy on performance...
    Вот так можно собрать все ключи, что есть в localStorage:
    const storageKeys = Array.from({length: localStorage.length}, (_, i) => localStorage.key(i));
    console.log(storageKeys);
    Ответ написан
  • Как исправить ошибку: You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file?

    bingo347
    @bingo347 Куратор тега JavaScript
    Crazy on performance...
    test: '/\.png$/'
    ковычки лишние
    test: /\.png$/
    Ответ написан
    Комментировать
  • Почему не работает делегирование JS?

    bingo347
    @bingo347 Куратор тега JavaScript
    Crazy on performance...
    вместо проверки className, который содержит строку со всеми классами элемента, проверьте:event.target.classList.contains('delete__button')
    Ответ написан
    Комментировать
  • Как отобразить нужный контент без медиа-запросов?

    bingo347
    @bingo347 Куратор тега JavaScript
    Crazy on performance...
    Можно так же медиа запросами:
    https://developer.mozilla.org/ru/docs/Web/API/Wind...
    Можно даже подписаться на изменение состояния по медиа запросу:
    https://developer.mozilla.org/en-US/docs/Web/API/M...
    Ответ написан
    Комментировать
  • Как правильно обновить общую сумму?

    bingo347
    @bingo347 Куратор тега JavaScript
    Crazy on performance...
    Дело в этой строке:
    total.innerHTML = Number(item.textContent) + Number(item.textContent);
    в total по сути выводится последний элемент умноженный на 2.

    Ну и хранить данные в DOM, и каждый раз парсить из текста числа - так себе идея.
    Хранити данные в переменных и в виде чисел, а DOM используйте только для отрисовки.
    Ответ написан
    Комментировать
  • Как сделать счетчик числа с 0% до 100%?

    bingo347
    @bingo347 Куратор тега JavaScript
    Crazy on performance...
    Комментировать
  • Как сделать, чтобы видео работало, не открываясь в отдельном окне?

    bingo347
    @bingo347 Куратор тега JavaScript
    Crazy on performance...
    Комментировать
  • Как вывести все значения по ключу находящиеся в массиве?

    bingo347
    @bingo347 Куратор тега JavaScript
    Crazy on performance...
    arr.flatMap(el => el.region).flatMap(el => el.cities).map(el => el.name)
    Ответ написан
    Комментировать
  • Почему у Null и Undefined нет прототипа и почему у других типов они именно такие?

    bingo347
    @bingo347 Куратор тега JavaScript
    Crazy on performance...
    Потому что прототипы есть только у объектов, то есть у сущностей (entity) для которых верноtypeof entity === 'object'Правда тут есть одно исключение - в спеке нет типа function, а во всех реализациях он есть по историческим причинам. То есть с точки зрения спеки:typeof function(){} === 'object'а на деле:typeof function(){} === 'function'Поэтому правильное тождество будет такое:
    typeof entity === 'object' || typeof entity === 'function'


    У остальных типов прототипа нет, в чем можно легко убедится
    1 instanceof Number
    1 instanceof Object
    'a' instanceof String
    'a' instanceof Object
    true instanceof Boolean
    true instanceof Object
    1n instanceof BigInt
    1n instanceof Object
    Symbol() instanceof Symbol
    Symbol() instanceof Object
    null instanceof Object
    undefined instanceof Object
    все эти выражения ложны, а как известно instanceof проверяет именно прототипы.

    А для того чтоб работали методы для большинства этих типов есть объектные обертки, к которым присутствует неявное приведение при обращении к свойствам через точку или квадратные скобки. Эти обертки не сложно получить передав данные примитивы в Object как функцию, а не как конструктор:
    Object(1) instanceof Number
    Object(1) instanceof Object
    Object('a') instanceof String
    Object('a') instanceof Object
    Object(true) instanceof Boolean
    Object(true) instanceof Object
    Object(1n) instanceof BigInt
    Object(1n) instanceof Object
    Object(Symbol()) instanceof Symbol
    Object(Symbol()) instanceof Object
    все эти выражения истинны.
    Однако надо понимать, что обертка - это уже не примитив:
    typeof Object(1) === 'object'
    typeof Object('a') === 'object'
    typeof Object(true) === 'object'
    typeof Object(1n) === 'object'
    typeof Object(Symbol()) === 'object'
    это все так же истинно.

    А вот у юнит-типов (типов-значений) null и undefined таких оберток нет. И хотя Object(null) и Object(undefined) возвращают просто пустой объект, обратится к методам null и undefined нельзя:
    null.property // TypeError: Cannot read property 'property' of null
    undefined.property // TypeError: Cannot read property 'property' of undefined
    Ответ написан
    Комментировать
  • Изменение строки JS replace регулярным выражением?

    bingo347
    @bingo347 Куратор тега JavaScript
    Crazy on performance...
    Если строка действительно такого простого вида, то регулярки - непростительное расточительство ресурсов.
    '2020-09-30'.split('-').reverse().join('.')
    Ну а если в большой строке заменить, то:
    '2020-09-30'.replace(/(\d{4})-(\d{2})-(\d{2})/, '$3.$2.$1')
    Ответ написан
    1 комментарий
  • Устарел ли getElementsBy* и чем лучше querrySelector?

    bingo347
    @bingo347 Куратор тега JavaScript
    Crazy on performance...
    Вот народ ушел в спор о производительности, но никто даже не попытался разобраться, а что под капотом... Производительность ведь искусственными бенчмарками меряют, ага...
    Что ж, времена сейчас такие
    многие на работу кодеров берут, которые даже интереса не имеют в глубь копать. Инженеров брать... - это устаревший подход, как выразился автор "популярного сайта", который прочел автор вопроса. Инженеры они дорогие, и найти их сложно, лучше кодер, пусть и не желающий на работе мозг включать, не говоря уж о желании в устройстве инструментов разбираться.

    Говорить о том, что некие фичи устарели - крайне глупо, если знать, что они ведут себя иначе, чем более модные альтернативы. Предлагаю немного разобраться и начать с того что на поверхности:
    - getElementById и querySelector возвращают конкретную ноду в единственном экземпляре
    - querySelectorAll и getElementsByName возвращает статичную коллекцию NodeList
    - getElementsByClassName, getElementsByTagName и getElementsByTagNameNS возвращают динамическую коллекцию HTMLCollection
    Как видим результат у разного апи различен, а значит и говорить, что некоторые из них устарели - глупо.
    Правда тут есть забавный момент
    в спеке HTMLCollection отмечен как "исторический артефакт", который не стоит использовать при проектировании нового апи. Но заметка эта не для веб-разработчиков, а для тех кто проектирует новое DOM апи.

    С устареванием вроде разобрались, но в вопросе еще есть часть "чем лучше". И тут есть теория, что getElementsBy* быстрее querySelector*. Чисто теоретически звучит логично, querySelector* должен делать полный поиск по дереву со сложностью O(n), а getElementsBy* могут использовать индексы на базе HashMap и получать данные со сложностью O(1), тут и особенности HTMLCollection будут кстати, так как можно не копировать коллекцию, а отдавать одну и ту же (и браузеры действительно так делают). Но без пруфов теория так и останется теорией.
    И бенчмарки - так себе пруф
    Хотя направлять инвесторов в нужную сторону - самое то. Проблема бенчмарков, что можно написать их так, что любая из сравниваемых сторон заметно обгонит другую. Дело техники. Например BubbleSort с O(n2) при определенных условиях в чистую уделывает MergeSort и QuickSort с их O(n×log2n), а именно при n=20 или меньше, 400 простых memswap в наглую рвут 87 рекурсивных операций с memcpy внутри
    Гораздо лучше тут выглядят исходники. И я выбрал исходники chromium, как самого распространенного:
    - getElementsBy* методы все как один обращаются к некой шаблонной функции EnsureCachedCollection, которая в свою очередь обращается к некоему NodeLists кэшу, устроенному внутри действительно как HashMap или что-то наподобие. Никакого поиска тут нет, просто берутся готовые значения, сложность у всего этого действительно константная O(1).
    - querySelector* используют абстракцию SelectorQuery, которая и в самом деле делает поиск по DOM. Но данный поиск неплохо оптимизирован и обвешан кэшами. Притом CSSOM использует абсолютно тот же алгоритм поиска DOM нод для каждого селектора в css.
    Для примера
    в подключенных на странице этого вопроса стилях более 1600 правил (некоторые из которых потенциально могут содержать несколько селекторов), полная обработка стилей из этого файла заняла на моей машине (Ryzen 3600 в стоке) чуть больше 9 мс. Если все это немного округлить, то понадобится 15000 querySelectorAll подряд, притом с разными селекторами, чтоб был промах кэша, дабы я ощутил заметную глазу задержку в ~100мс


    На этом спор думаю можно закрыть. querySelector* методы действительно могут быть медленнее, но чтоб убить ими производительность, нужно очень постараться. На фоне того, что пишут кривые ручки среднестатистического дешевого js-кодера это будет незначительной потерей измеряемой в наносекундах. Используйте то что удобнее в каждой конкретной ситуации.
    Ответ написан
    1 комментарий
  • Как отсортировать массив массивов строк в js?

    bingo347
    @bingo347 Куратор тега JavaScript
    Crazy on performance...
    Данная задача в принципе не решается сортировкой, так как сортировка это про отношение больше/меньше/равно, которого в данном случае нет.
    Самое простое здесь, это построить из этих элементов двусвязный список, а затем преобразовать его в результирующий массив:
    function orderArray(arr) {
      // для начала построим ноды списка и соберем их в 2 HashMap по обоим строкам
      const maps = arr.reduce((acc, item) => {
        const node = {item, next: null, prev: null};
        acc[0][item[0]] = node;
        acc[1][item[1]] = node;
        return acc;
      }, [{}, {}]);
    
      // после пройдемся по обоим HashMap и соединим связи
      for(const key of Object.keys(maps[0])) {
        maps[0][key].next = maps[0][maps[0][key].item[1]] || null;
      }
      for(const key of Object.keys(maps[1])) {
        maps[1][key].prev = maps[1][maps[1][key].item[0]] || null;
      }
    
      // найдем начальную ноду списка (ноду без предыдущей ноды)
      let cur = Object.values(maps[0]).find(({prev}) => prev === null);
    
      // и начиная с нее соберем список в массив
      const result = [];
      while(cur) {
        result.push(cur.item);
        cur = cur.next;
      }
    
      return result;
    }
    
    console.log(orderArray([['butter', 'jelly'], ['bananas', 'apples'], ['peanuts', 'butter'], ['jelly', 'bananas']]));
    Ответ написан
    1 комментарий
  • Могу ли я в чистом javascript в асинхронной функции подождать возникновения события?

    bingo347
    @bingo347 Куратор тега JavaScript
    Crazy on performance...
    Первое что нужно понимать, что async/await - это всего лишь синтаксический сахар над промисами, а значит имеет все особенности работы с ними:
    Во-первых, промис может разрешится (или отклонится, но в этой задаче reject не нужен) только единожды. Это сильно отличает их от событий, которые могут происходить многократно.
    Во-вторых, промисы обрабатываются на особой фазе event loop называемой microtasks, что опять же отличается от событий, которые выполняются на другой фазе (tasks). Это означает, что обработка промиса (колбэк метода then, или сахар над ним в виде await оператора) всегда произойдет асинхронно от возникновения события. Это накладывает ряд ограничений, например не получится сделать preventDefault у объекта или не получится запустить действия требующие синхронной работы из trusted события (play не muted video/audio, вход в фулскрин и тд).

    В простом варианте можно слушать событие разово, для чего можно использовать параметр once. Так же для оптимизации можно использовать параметр passive, так как нам все равно поздно вызывать preventDefault, а в некоторых случаях это может дать нам оптимизацию. Ну и управлять параметром capture попросту бесполезно, так как обработка будет в любом случае после обеих фаз. В итоге для этого можно пользоваться такой функцией хелпером:
    function listenOnce(target, event) {
        return new Promise(resolve => {
            target.addEventListener(event, resolve, {
                once: true,
                passive: true
            });
        });
    }
    
    // использование
    const event = await listenOnce(document, 'DOMContentLoaded');
    console.log(event);


    Если же нужно слушать событие многократно, то разумно обернуть прослушивание события в асинхронный итератор. Здесь так же присутствуют все ограничения связанные с промисами, но за счет того, что у нас будет итератор по множеству промисов, мы сможем слушать событие многократно. так же тут нужно предусмотреть возможность отписаться от события. Хелпер для данного случая получится такой:
    function listen(target, event) {
        let currentResolve = () => {};
        const handler = event => currentResolve({value: event, done: false});
        return {
            [Symbol.asyncIterator]() {
                target.addEventListener(event, handler, {passive: true});
                return {next: () => new Promise(resolve => {
                    currentResolve = resolve;
                })};
            },
            stop() {
                target.removeEventListener(event, handler);
                currentResolve({done: true});
            }
        };
    }
    
    // использование
    for await(const event = listenOnce(window, 'scroll')) {
        console.log(event);
    }
    
    // с отпиской
    const eventIterator = listenOnce(window, 'scroll');
    let count = 10;
    for await(const event = eventIterator) {
        console.log(event);
        if(--i === 0) {
            eventIterator.stop();
        }
    }
    Ответ написан
    4 комментария
  • Почему функции могут не работать?

    bingo347
    @bingo347 Куратор тега JavaScript
    Crazy on performance...
    + к ответу WbICHA:
    yp += speed;
    какого поведения Вы ожидаете от присваивания в аргумент?
    Ответ написан
    8 комментариев
  • Можно ли код babel конвертировать обратно?

    bingo347
    @bingo347 Куратор тега JavaScript
    Crazy on performance...
    Ответ написан
    Комментировать
  • Как сделать наследование классов ES6?

    bingo347
    @bingo347 Куратор тега JavaScript
    Crazy on performance...
    class Dropdown() {
      constructor(selector) {
      	this.$el = document.querySelector(selector);
      }
    
      toggle(cls) {
      	this.$el.classList.toggle(cls);
      }
    }
    
    class Nav extends Dropdown {
      constructor(selector) {
      	super(selector);
      	this.$btn = this.$el.querySelector('.nav-button');
        	this.$btn.addEventListener('click', this.toggle.bind(this, 'nav_open'));
      }
    }
    
    class Menu extends Dropdown {
      constructor(selector) {
      	super(selector);
      	this.$btn = this.$el.querySelector('.menu-button');
        	this.$btn.addEventListener('click', this.toggle.bind(this, 'menu_open'));
      }
    }
    
    new Nav('.nav');
    new Menu('.menu');
    так?
    Ответ написан
    2 комментария
  • Как правильно вывести такой код в innerHtml?

    bingo347
    @bingo347 Куратор тега JavaScript
    Crazy on performance...
    // небольшая функция хелпер, чтоб писать меньше кода:
    const appendNewDiv = parent => parent.appendChild(document.createElement('div'));
    
    // создадим основные эллементы
    const wrap = appendNewDiv(albumTrack);
    const title = appendNewDiv(wrap);
    const itemAlbumWrap = appendNewDiv(wrap);
    const itemAlbum = appendNewDiv(itemAlbumWrap);
    
    wrap.classList.add('album__item');
    title.classList.add('album_title');
    itemAlbumWrap.classList.add('album__item__wrap');
    itemAlbum.classList.add('album__photo');
    
    // сюда будем складывать элементы, которые должны удалятся при смене number
    let elements = [];
    
    // функция, через которую меняем отображаемые данные по number
    const renderByNumber = number => {
        const {albumId, albums} = arr2[number];
        elements.forEach(e => wrap.removeChild(e));
        title.textContent = `Альбом ${albumId}`;
        elements = albums.map(album => {
            const e = appendNewDiv(wrap);
            e.textContent = album.title;
            return e;
        });
    };
    
    // при изменении number теперь просто вызываем
    renderByNumber(number);
    Ответ написан
    3 комментария
  • Как округлить число в Javascript до N знаков после запятой?

    bingo347
    @bingo347 Куратор тега JavaScript
    Crazy on performance...
    Комментировать
  • Отсортировать массив и вернуть bolean?

    bingo347
    @bingo347 Куратор тега JavaScript
    Crazy on performance...
    const isSorted = arr => arr.every((el, i) => {
      if(i === 0) { return true; }
      const prev = arr[i - 1];
      const elType = typeof el;
      const prevType = typeof prev;
      if(elType === prevType) {
          return el >= prev;
      }
      return elType === 'string' && prevType === 'number'
    });
    isSorted([0, 1, 2, 2, 2, 3, 'a', 'b', 'w']); // true
    isSorted([0, 3, 1, 2, 2, 2, 'a', 'b', 'w']); // false
    isSorted(['a', 1, 2, 2, 2]); // false
    isSorted([1, 2, 2, 0, 'a']); // false
    Ответ написан
  • Почему не выполняется every?

    bingo347
    @bingo347 Куратор тега JavaScript
    Crazy on performance...
    У HTMLCollection нет метода every, и метода includes тоже нет, как и метода some у DOMElement
    Ответ написан
    7 комментариев