Задать вопрос
  • Является ли данный код нарушением принципа Dependency Inversion?

    Мне кажется, тут достаточно фабричного метода, ведь создается лишь одна сущность Post
    Абстрактная фабрика все таки про набор связных сущностей
  • Как сделать условие JavaScript с помощью setinterval?

    bingo347
    @bingo347 Куратор тега JavaScript
    Flipflap, так проверяйте URL при входе на страницу, ну или перехватывайте роутинг если у Вас SPA
  • Почему не удаляется событие?

    bingo347
    @bingo347 Куратор тега JavaScript
    Bavashi, я тут на целую лекцию наговорить/написать могу... но постараюсь как то выжать основные тезисы своей позиции, надеюсь смысл останется понятен и мой посыл будет интерпретироваться с тем же смыслом, что я в него вкладываю.
    Начну пожалуй с такого наблюдения, что когда говорят о 3 китах ООП (наследование, инкапсуляция и полиморфизм), неважно на собеседованиях, докладах или еще где-то, - для меня это верный признак, что ООП у людей нет и они его не особо то понимают. Я не знаю откуда пошла связь наследования, инкапсуляции и полиморфизма с ООП, но я абсолютно уверен, что это не связанные вещи, хоть и могут использоваться вместе. Я думаю, большую роль здесь играет, что люди в большинстве своем интерпретируют одни и те же термины по разному, более того многие из них способны вполне аргументировано доказать, что именно их интерпретация верная. И так как я тут обосновываю свою точку зрения "наследование в ООП вторично" (а первичны композиция объектов и их взаимодействие), пожалуй стоит, что я подразумеваю под терминами "наследование", "инкапсуляция" и "полиморфизм", начну пожалуй с последнего:

    Полиморфизм - это инструмент уменьшения сложности программного кода и упрощения его поддержки путем создания абстракций переиспользуемых для разных типов данных. И я знаю как минимум 3 типа полиморфизма, хотя возможно их существует больше:
    - Параметрический полиморфизм - это когда мы передаем в некоторую абстракцию свой тип данных в качестве параметра, а абстракция изменяется, подстраиваясь под этот тип данных. Иногда параметризуемый тип данных может быть ограничен некоторым контрактом. На выходе мы можем получать практически любые сущности языка - функции, классы, типы данных. В большинстве языков данный тип представлен дженериками, но могут быть и другие реализации (например темплейты в C++).
    - Ad-Hoc полиморфизм - это когда мы можем представить несколько реализаций абстракции под одним именем, а компилятор сам решит какую из них использовать в каждом конкретном случае на основе некоторых признаков. Чаще всего таким признаком служат различные наборы параметров при обращении к сущности. Самый яркий пример тут - это перегрузка функций, но опять же могут быть другие варианты (например различные реализации типажа у разных типов в Rust).
    - Полиморфизм подтипов - это когда мы, зная некий общий тип, можем использовать через него данные любого его подтипа. Это как раз тот тип, который подразумевают, когда говорят об ООП. Представлен как правило наследованием и/или реализацией интерфейсов, но так же может быть представлен и другими способами (например структурная типизация в TypeScript или утиная типизация в Go).
    Полиморфизм встречается в большинстве парадигм - в ООП, в ФП, в процедурщине и т.д.

    Наследование - это инструмент уменьшения сложности программного кода и упрощения его поддержки путем переиспользования сущностей в подтипе из супертипа. Уже из определения можно сделать вывод, что это всего лишь частная реализация полиморфизма подтипов. А еще в моем определении нет ни слова про ООП, и не просто так, многие, ошибочно (ИМХО), считают, что наследование - это детище ООП. Чтоб понять, что это не так, достаточно обратится к истории - ООП зародился в 1969 году с языком Smalltalk, а увидел свет и того позже, а наследование появилось на 2 года раньше в 1967 в языке Simula. И хотя в википедии приписывают Simula к ООП языку (сами понимаете, кто пишет википедию, там даже про JS с дуру пишут, что у него утиная типизация, не понимая ее сути), но Simula - типичный пример смеси процедурной и структурной парадигм. Хотя многие прекрасно пишут в стиле Simula на Java или C#, в процедурно-структурной парадигме, и свято верят, что это ООП, так что заблуждение не случайно.
    Наследования обычно нет в ФП, ибо функции плохо наследуются друг от друга, но вот в процедурной парадигме оно вполне себе может быть, наравне с ООП.

    Инкапсуляция - это инструмент уменьшения сложности программного кода и упрощения его поддержки путем (где-то я уже это писал...) сокрытия этой самой сложности. И опять таки, это не про private модификатор метода в ООП, хотя private и может преследовать своей целью инкапсуляцию, как и замыкания, как и разбиения на модули, которые экспортируют не все подряд, а только то, что явно укажет разработчик. Инкапсуляция - это про интерфейсы, есть интерфейсы доступные всем, но позволяющие сделать ограниченный набор действий с абстракцией, а есть внутренние интерфейсы, через которые абстракция управляет своим состоянием на все 100%, но использующему коду их знать не зачем, иначе он станет слишком сложным и подверженным багам. А еще инкапсуляция - это про гарантии единственного источника истины.
    Инкапсуляция встречается в большинстве парадигм - в ООП, в ФП, в процедурщине и т.д.

    Ну и на последок, выскажу немного своего мнения. Я считаю наследование плохой практикой. Те способы, которыми оно предлагает упростить поддержку кода, могут иметь обратный эффект, так как в неумелых руках наследование очень легко подводит разработчика к нарушению SRP из SOLID, скрывая это нарушение за цепочкой наследования. Кроме того нет адекватных способов унаследоваться от нескольких классов сразу или предоставить наследуемый класс через inversion of control и dependency injection. Вместо наследования я предлагаю использовать композицию, которая не имеет данных недостатков, но кроме того гораздо ближе по духу ООП, а так же будет понятна большему числу разработчиков, так как выглядит практически одинаково и в ООП и в ФП парадигме.
  • Почему не работает es6 в ie11?

    bingo347
    @bingo347 Куратор тега JavaScript
    Сергей Бурдужа, env пресету нужно указать, в каких окружениях наш код будет использоваться
  • Почему не удаляется событие?

    bingo347
    @bingo347 Куратор тега JavaScript
    fgehte, да, ООП весьма и весьма многословен, зато это обеспечивает поддерживаемость и расширяемость кода. ФП решает ту же задачу, кода будет меньше, но вот сложность освоения ФП людьми с "процедурным" мышлением гораздо выше. Однако мне лично нравится ФП стиль за свою предсказуемость и надежность, при этом сохраняя ту гибкость и расширяемость, что дает ООП.
    Я набросал тот же самый пример в ФП стиле и снабдил комментариями
    // Так как в ФП большая часть кода должны быть чистыми функциями
    // все сайд эффекты придется выносить к краю и выполнять там
    // таким "краем" будет такая обертка:
    const withSideEffects = (runner) => (...args) => {
        const [returnValue, sideEffects] = runner(...args);
        sideEffects.forEach(se => se());
        return returnValue;
    };
    
    // А чтоб доносить сайд эффекты до этой функции
    // их нужно держать в изменяемом состоянии
    // чистая функция может менять только свое локальное состояние
    // инкапсулируем эту логику в таком функторе:
    const createSideEffectAccumulator = () => {
        const sideEffects = [];
        const acc = {
            with: (sideEffect) => (
                sideEffects.push(sideEffect),
                acc
            ),
            map: ([returnValue, resolvedSideEffects]) => (
                sideEffects.push(...resolvedSideEffects),
                returnValue
            ),
            resolve: (returnValue) => [returnValue, sideEffects]
        };
        return acc;
    };
    
    // подписка на событие (и отписка) - это сайд эффект
    // а вот функция возвращающая парочку сайд эффектов
    // созданных на основе своих аргументов - вполне себе чистая:
    const subscriptionFactory = (target, eventName, listener) => [
        () => target.addEventListener(eventName, listener),
        () => target.removeEventListener(eventName, listener)
    ];
    
    // изменение стиля - тоже сайд эффект,
    // воспользуемся тем же приемом:
    const setDisplayStyleFactory = (target, value) => () => target.style.setProperty('display', value);
    
    // а еще для нашей задачи нужны условия
    // абстрогируем их в функцию:
    const condition = (predicate, truthyCallback, falsyCallback) => (...args) => (predicate(...args)
        ? truthyCallback(...args)
        : falsyCallback(...args)
    );
    
    // для falsyCallback в нашей задаче понадобится фабрика пустых аккумуляторов сайд эффектов:
    const emptySideEffectAccumulatorFactory = () => createSideEffectAccumulator().resolve();
    
    // наша логика в виде чистых функций:
    const openPopup = (modal, subscribeDocumentKeydown) => () => (createSideEffectAccumulator()
        .with(setDisplayStyleFactory(modal, 'block'))
        .with(subscribeDocumentKeydown)
        .resolve()
    );
    const closePopup = (modal, unsubscribeDocumentKeydown) => () => (createSideEffectAccumulator()
        .with(setDisplayStyleFactory(modal, ''))
        .with(unsubscribeDocumentKeydown)
        .resolve()
    );
    const onPopupCloseEscPress = (modal, unsubscribeDocumentKeydown) => condition(
        event => event.key === 'Escape',
        closePopup(modal, unsubscribeDocumentKeydown),
        emptySideEffectAccumulatorFactory
    );
    const subscribeTriggers = (modal, triggers, subscribeDocumentKeydown) => () => triggers.reduce(
        (acc, trigger) => acc.with(subscriptionFactory(trigger, 'click', withSideEffects(openPopup(modal, subscribeDocumentKeydown)))[0]),
        createSideEffectAccumulator()
    ).resolve();
    
    // соберем сайд эффекты произведенные нашим кодом
    const bindModal = withSideEffects((modal, triggers, closeModalBtn) => {
        const acc = createSideEffectAccumulator();
        const [
            subscribeDocumentKeydown,
            unsubscribeDocumentKeydown
        ] = subscriptionFactory(document, 'keydown', withSideEffects(subscribeTriggers(modal, triggers, () => subscribeDocumentKeydown())))
        return (acc
            .with(subscriptionFactory(modal, 'click', withSideEffects(condition(
                event => event.target === modal,
                closePopup(modal, unsubscribeDocumentKeydown),
                emptySideEffectAccumulatorFactory
            )))[0])
            .with(subscribeTriggers(modal, triggers, subscribeDocumentKeydown))
            .with(subscriptionFactory(closeModalBtn, 'click', withSideEffects(closePopup(modal, unsubscribeDocumentKeydown)))[0])
            .resolve()
        );
    });
    
    // для выполнения нужно будет передать зависимости
    bindModal(modal, triggers, closeModalBtn);

    Если убрать комментарии, то в ФП варианте 75 строк кода, против 173 в ООП, но если в ООП могут разобраться многие, то вот с ФП поначалу приходится напрягать мозг.
  • Почему не удаляется событие?

    bingo347
    @bingo347 Куратор тега JavaScript
    Я правильно понимаю, что если передать как:document.addEventListener(`click`, listener)то в функцию/метод event попадает, т.е. его не обязательно передавать так как делал я?
    fgehte, да, нет принципиальной разницы, сохранили Вы ссылку на функцию в переменную и потом передали аргументом, или сразу передали аргументом.

    А как должен выглядить код в таком случае?
    P.s. При описание вопроса я указал, что удалил второстепенную информаци такую как конструктор, наследование и тд.
    ООП - это про композицию объектов и их взаимодействие через вызовы методов, наследование вторично.
    ООП решение Вашего кейса выглядит примерно так
    class EventSubscription {
        constructor(target, eventName, eventListener) {
            this.target = target;
            this.eventName = eventName;
            this.eventListener = eventListener;
        }
    
        subscribe() {
            this.target.addEventListener(this.eventName, this.eventListener);
        }
    
        unsubscribe() {
            this.target.removeEventListener(this.eventName, this.eventListener);
        }
    }
    
    class SubscriptionCollection {
        constructor(subscriptions) {
            this.subscriptions = subscriptions;
            this.subscription = this;
        }
    
        subscribe() {
            this.subscriptions.forEach(subscription => subscription.subscribe());
        }
    
        unsubscribe() {
            this.subscriptions.forEach(subscription => subscription.unsubscribe());
        }
    }
    
    class FilterEventListenerDecorator {
        constructor(filter, eventListener) {
            this.filter = filter;
            this.eventListener = eventListener;
        }
    
        handleEvent(event) {
            const {filter} = this;
            if(!filter(event)) { return; }
            return this.eventListener.handleEvent(event);
        }
    }
    
    class RunEventListener {
        constructor(runner) {
            this.runner = runner;
        }
    
        handleEvent() {
            this.runner.run();
        }
    }
    
    class RunnerCollection {
        constructor(...runners) {
            this.runners = runners;
        }
    
        run() {
            this.runners.forEach(runner => runner.run());
        }
    }
    
    class StyleSetter {
        constructor(target, property, value) {
            this.target = target;
            this.property = property;
            this.value = value;
        }
    
        set() {
            this.target.style.setProperty(this.property, this.value);
        }
    }
    
    class StyleSetterRunnerDecorator {
        constructor(styleSetter) {
            this.styleSetter = styleSetter;
        }
    
        run() {
            this.styleSetter.set();
        }
    }
    
    class SubscriberRunnerDecorator {
        constructor(subscriber) {
            this.subscriber = subscriber;
        }
    
        run() {
            this.subscriber.subscribe();
        }
    }
    
    
    class UnsubscriberRunnerDecorator {
        constructor(unsubscriber) {
            this.unsubscriber = unsubscriber;
        }
    
        run() {
            this.unsubscriber.unsubscribe();
        }
    }
    
    class NextTickApplyRunnerDecorator {
        constructor(runnerFactory) {
            this.runner = null;
            Promise.resolve().then(() => {
                this.runner = runnerFactory();
            });
        }
    
        run() {
            if(!this.runner) { return; }
            this.runner.run();
        }
    }
    
    class DocumentKeydownSubscriptionFactory {
        constructor(modal) {
            this.subscription = new EventSubscription(document, 'keydown', new FilterEventListenerDecorator(
                event => event.key === `Escape`,
                new RunEventListener(new RunnerCollection(
                    new NextTickApplyRunnerDecorator(() => new UnsubscriberRunnerDecorator(this.subscription)),
                    new StyleSetterRunnerDecorator(new StyleSetter(modal, 'display', ''))
                ))
            ));
        }
    }
    
    class ModalClickSubscriptionFactory {
        constructor(modal, documentKeydownSubscription) {
            this.subscription = new EventSubscription(modal, 'click', new FilterEventListenerDecorator(
                event => event.target === modal,
                new RunEventListener(new RunnerCollection(
                    new UnsubscriberRunnerDecorator(documentKeydownSubscription.subscription),
                    new StyleSetterRunnerDecorator(new StyleSetter(modal, 'display', ''))
                ))
            ));
        }
    }
    
    class CloseModalBtnSubscriptionFactory {
        constructor(closeModalBtn, modal, documentKeydownSubscription) {
            this.subscription = new EventSubscription(closeModalBtn, 'click', new RunEventListener(new RunnerCollection(
                new UnsubscriberRunnerDecorator(documentKeydownSubscription.subscription),
                new StyleSetterRunnerDecorator(new StyleSetter(modal, 'display', ''))
            )));
        }
    }
    
    class TriggerSubscriptionFactory {
        constructor(trigger, modal, documentKeydownSubscription) {
            this.subscription = new EventSubscription(trigger, 'click', new RunEventListener(new RunnerCollection(
                new SubscriberRunnerDecorator(documentKeydownSubscription.subscription),
                new StyleSetterRunnerDecorator(new StyleSetter(modal, 'display', 'block'))
            )));
        }
    }
    
    function bindModal(modal, triggers, closeModalBtn) {
        const documentKeydownSubscription = new DocumentKeydownSubscriptionFactory(modal);
        new SubscriptionCollection([
            new ModalClickSubscriptionFactory(modal, documentKeydownSubscription),
            new CloseModalBtnSubscriptionFactory(closeModalBtn, modal, documentKeydownSubscription),
            new SubscriptionCollection(triggers.map(trigger => new TriggerSubscriptionFactory(trigger, modal, documentKeydownSubscription)))
        ]).subscribe();
    }
    
    bindModal(modal, triggers, closeModalBtn);

    ФП - это композиция чистых функций. Чистых значит, что они без побочных эффектов (вроде подписки на события или изменения стилей) и для одних и тех же аргументов всегда возвращающие один и тот же результат. Побочные эффекты выносятся в монадах (контейнерах) к краю программы и выполняются там.
  • Как скрестить webpack и бэм?

    sir_pounce, БЭМ - всего лишь идея, притом пропагандирующие БЭМ даже не уточняют, а какой БЭМ они пропагандируют. А на просторах сети можно найти с десяток интерпретаций, некоторые из которых противоречат друг другу. Инструментария - ноль, придется все руками писать, а как следствие от ошибок Вас тоже никто не застрахует.
    Лично я встречал в живых проектах несколько разновидностей БЭМа, и только в 1 случае не было больших проблем с поддержкой всей этой каши стилей.
    А css-modules - это конкретный инструмент, который решает ту же самую задачу, но только в автоматическом режиме. Притом он гарантирует Вам отсутствие конфликтов стилей.
    Решать Вам что выбрать
  • Вычисление строк и столбцов которые монотонно спадают?

    Vladimir S, да, я под "для любой" подразумевал именно "для каждой"
    поправил ответ, чтоб не было неоднозначности
  • Прокинуть Props с использование TS?

    bingo347
    @bingo347 Куратор тега TypeScript
    groulls, если передаете как объект, то эти имена будут ключами объекта
  • Как сделать POST запрос, имею на руках данную инфу(см. ниже)?

    bingo347
    @bingo347 Куратор тега JavaScript
    Я бы только encodeURIComponent к значениям добавил, ибо туда думаю из переменных данные будут подставляться
  • Почему может падать программа с exit code: 0xc0000005, STATUS_ACCESS_VIOLATION?

    priority, вообще ACCESS_VIOLATION в Rust не встречал ни разу, компилятор не дает его сделать без хаков с unsafe
    Но из C чет припоминается, что это про доступ к выделенной, но неинициализированной памяти
  • Отправка формы в iframe(в самом iframe есть форма) - как?

    bingo347
    @bingo347 Куратор тега JavaScript
    Ну во-первых, стоит показать код, без него остается только гадать на кофейной гуще, что не так.
    А в-нулевых, отладчик Вам в помощь: жмем ctrl/cmd+shift+I, переходим на вкладку sources, ставим точку останова
  • Как писать хороший и расширяемый css код?

    bingo347
    @bingo347 Куратор тега HTML
    Честно говоря, мне уже давно не интересны подобные дискуссии. Потому что аргументы у противника всегда одни и те же, проблемы всегда надуманные.
    Сергей delphinpro, вот только эти "надуманные проблемы" из раза в раз встречаются в реальных проектах.
    Проблема не в том, что БЭМ какой-то там плохой, в свое время он неплохо решал проблему конфликтов в css, так как инструментов для этого не было. Сейчас есть инструменты решающие эту проблему, и решают они ее определенно лучше, хотя бы по тому, что менее подвержены человеческому фактору. Но даже это не проблема, поле можно вспахать лопатой, если Вы боитесь использовать трактор.
    Настоящая проблема в том, что новичку советуют непонятные три буквы, без объяснения что это такое. Притом если загуглить эти три буквы, то можно найти с десяток различных толкований. chekoff пришел с конкретным вопросом, как он может сделать свой код более поддерживаемым, но вместо этого получает маркетинг/религию/пропаганду идеи, бездумное использование которой приведет его ровно к обратному эффекту - неподдерживаемому нечто...
  • Как писать хороший и расширяемый css код?

    bingo347
    @bingo347 Куратор тега HTML
    Сергей delphinpro, нет, реально несколько мегабайт вышло тупо на манглинге имен классов
    .some-wtf-class1 .some-wtf-class2 .some-wtf-class3 {}
    тоже что-то из ряда вон выходящее, как раз говорящее о кривых ручках написавшего стили...
    .app-header__logo {} — вполне очевидно, что это блок, содержащий логотип и расположенный в шапке сайта
    может и очевидно, вот только где разметка и логика этого блока по прежнему неизвестно, как и наоборот, из разметки найти ее стили невозможно.
    Ладно Ctrl+click Вы в моей IDE убили. Но и поиском воспользоваться нормально нельзя, ибо на деле это выглядит так:
    .app-header {
      // лютая портянка стилей
      &__logo {}
      // лютая портянка стилей
    }
  • Как писать хороший и расширяемый css код?

    bingo347
    @bingo347 Куратор тега HTML
    Виталий, Сергей delphinpro, я может тоже не познал дзен БЭМа, зато пару раз хлебнул горя и сорвал сроки разгребая кашу, наподобие той, что на скринах у Хороший UI в ответе. Пойди разбери к чему эти стили относятся, как их адекватно бить на чанки...
    После разгребания этого всего и перехода на css-modules выходные стили не только стали биться на чанки, но и полегчали в сумме на несколько мегабайт, ибо были классы вида .some-wtf-block__some-wtf-element--some-wtf-modificator, а стали .dfs3f, а их в проекте были тысячи
  • Как писать хороший и расширяемый css код?

    bingo347
    @bingo347 Куратор тега HTML
    Хороший UI, Вам видимо ни разу не приходилось разбирать подобную кашу из стилей за другими людьми...
  • Как скрестить webpack и бэм?

    Владислав Лысков, интересно в чем дзэн? В том что на выходе стили и шаблоны весят в несколько раз больше? Или в невозможности адекватно это все поддерживать и переиспользовать?
  • Как бы вы реализовали вычисление списка "любимых эмодзи" пользователя?

    EchoStan, да, только второй индекс по 1 полю и не уникальный:
    CREATE INDEX *** ON top_used_emojis (usages_count DESC)