Pull to refresh

Comments 54

Хватит уже жути нагонять. Если убрать мутации и отменить ссылки на объекты, создавать приложения будет в сотни раз сложнее.
const egg = { name: "Humpty Dumpty" };
egg.isBroken = false;

Такой подход в целом и общем делает код медленнее и часто может приводить к неожиданным последствиям в будущем
Избегая такого код разработка станет дешевле в долгосрочной перспективе
Нужно добавить что такой код пишут только те, кому ураган, на память, в голове инородный предмет оставил. Ну или совсем новичок в программировании.
Тогда стоит добавить что авторов плагинов для плохо продуманных библиотек.
Зачем делать объекты динамическими, когда можно создать основу конфигурации в виде класса, наделив его базовым состояние, а авторам плагинов позволить его расширять?
Думать сейчас некогда. Херак херак и в продакшн Везде скрам cd аджайл
Поясните пожалуйста развернуто для тех кто с утра медленно думает 8(
Когда мы меняем newEgg (подвергаем объект мутации), автоматически меняется и egg. Вы знали об этом?

Я думал, каждый кто изучает джаваскрипт это узнает в первую очередь. Не?
Что если использовать… (spread) оператор? В Redux рекомендуют изменять данные в редьюсерах с помощью Object.assign или этого оператора. Он иммутабельный или нет?
Spread оператор это синтаксический сахар над Object.assign

const newEntity = { ...original, ...mutation }

Эквивалентно
const newEntity = Object.assign({}, original, mutation)
Речь шла только об одном конкретном случае — работа с объектами. Прошу прощения, стоило уточнить.
UFO just landed and posted this here

Фразу "объекты в JavaScript передаются по ссылке" способен произнести только тот программист, который никогда не работал с другими языками, где и правда существует передача по ссылке...

Вы про передачу аргументов по ссылке?
Да, согласен, в функцию даже ссылка на объект передаётся по значению (значением переменной объектного типа является ссылка).
А как правильно сформулировать процитированную мысль?
«Объекты относятся к ссылочному типу данных» или как-то так. Ну или «свойства объектов-параметров доступны по ссылке».

Боже, эта любовь к зависимостям…
deepFreeze:


const deepFreeze = o => {
    Object.freeze(o);
    return Object.getOwnPropertyNames(o).every(
        p => o.hasOwnProperty(p) && o[p] instanceof Object && !Object.isFrozen(o[p]) ? deepFreeze(o[p]) : true
    );
};

Можно ещё сразу же: deepFreeze(deepFreeze);

Зачем вы проверяете hasOwnProperty?


Кстати, проверка o[p] instanceof Object слишком опасная. Можно случайно вмешаться во внутреннюю структуру сложного класса и все поломать. Лучше проверять на Object.getPrototypeOf(o[p]) === Object.prototype.


Кстати, o[p] может оказаться вычисляемым свойством. Лучше получать дескриптор через Object.getOwnPropertyDescriptor и проверять его value.

Зачем вы проверяете hasOwnProperty?

А и правда, в сочетании с getOwnPropertyNames оно явно лишнее.


А вот про instanceof можно поподробнее? Впрочем, на сколько я понимаю проверять прототип тоже не самая здравая идея:
http://perfectionkills.com/instanceof-considered-harmful-or-how-to-write-a-robust-isarray/
Если в том примере с xArray добавить Object.getPrototypeOf(arr) === Array.prototype, то тоже будет false. Тогда уже лучше o !== null && typeof o === 'object', наверное?


Т.е. полный варианту будет выглядеть как-то так:


const deepFreeze = o => {
    Object.freeze(o);
    Object.getOwnPropertyNames(o).forEach(
        (p, i, o) => {
            let d = Object.getOwnPropertyDescriptor(o, p);
            if (d && d.value !== null &&
                typeof d.value === 'object' &&
                !Object.isFrozen(d.value))
                deepFreeze(d.value);
        }
    );
    return o;
};
Попробуйте применить ваш код к объекту Date или какому-нибудь HTMLElement… Или к модели mobx, компоненту React или еще чему-нибудь подобному.

Идея строгой проверки прототипа — убедиться, что на входе лежит именно литерал объекта, а не что-то более сложное.

Камон, если бы не React, который несмотря на название, совсем не реактивный, про иммутабельность никто бы и не вспомнил. И даже React с Mobx не нуждается в иммутабельных структурах.


Не мутации страшны, а неконтролируемые и неотслеживаемые мутации. vuex предупреждает разработчика — «не меняй состояние вручную», а в redux можно запросто это сделать и полдня потом дебажить, «какая сволочь стреляла».

Еще есть Object.seal.
Он такой же как freeze, только позволяет изменения существующим ключам.

Для полноты картины отмечу, что персистентные структуры данных сильно сложнее в написании и отладке, чем свои "рядовые" "братья". В лучшем случае они работают медленнее лишь в константу раз. Но проигрыш в скорости (например я не слышал о персистентном списке работающем за O(1)) может быть на больших объемах данных может быть очень серьезным.
Таким образом, не стоит пихать такие структуры туда, где идет много вычислений.

Вот список как раз — классика персистентных структур. { head: ..., tail: ...}


За O(1) у него работают операции "добавить элемент в начало" и "удалить первый элемент". Итерация по списку делается удалением всех элементов.

Вы привели пример персистентного стека, и он действительно работает за O(1).
Я же имею в виду обычный список типа массива, т.е. чтение/запись в произвольные места.
Лучшее, что мне известно в этой области — персистентные деревья, с запросами за O(log N) и большой константой.
А плюшки, которые дает персистентность (много версий струкутры с возможностью одновременной работы с ними), в типичном веб-приложении вряд ли нужны (например в Redux хранилище единственно, т.о. версия нужна одна).

  1. Object.freeze, Object.assign, ImmutableJS и прочие такие штуки серьёзно так замедляют работу приложения. Я бы рекомендовал воспользоваться TypeScript, который не позволит вам динамически изменить сигнатуру объекта или изменить readonly свойство. При этом ещё на стадии написания кода, а не в рантайме и соответственно без замедления исполнения.


  2. Объекты можно условно разделить на два типа: значения и контейнеры. Беда JS в том, что, например, один и тот же Array выступает и в роли контейнера (push, pop, ...) и в качестве значения (map, filter, ...).


  3. Никаких опасностей мутыций вы не продемонстрировали. Только описали азы языка и назвали их "опасными мутантами". Кстати, тот самый синий и волосатый монстр, о котором вы говорили, — весьма душевный человек и проницательный собеседник :-)
1. Решать проблемы JS перейдя на TS это так же как решать проблемы C++ перейдя на Java. Не всегда это оправданно
Согласен. Лучше просто знать язык и писать нормально. Хорошему программисту мутабельность не мешала никогда. Не хочешь изменять — не изменяй.
Нет, это как решать проблемы Си перейдя на С++.
Вот это хороший пример, потому, что у С++ есть некоторые Сишные проблемы

Java не компилируется в C++.

А это разве влияет на мое утверждение, что переход ради решения одной (зачастую несущественной) проблемы конкретным путем, не всегда целесообразно переходить на другой язык?
Очень интересно узнать, за что минусуют то? Я же не предлагаю писать все и всия на JS, а просто не писать на TS там, где он избыточен
UFO just landed and posted this here

Любые сущности в программировании можно разделить на 2 типа:


  • контейнеры — мы в них можем что-то положить, и что-то вынуть. Контейнер может изменяться, но от этого он не превращается в другой контейнер. Мы можем его куда-то передать в качестве значения и ожидаем, что он будет так же мутабелен. Контейнеры реализуются через ссылочные типы: собственно ссылки, объекты, их поля, просто переменные. Два контейнера могут содержать одинаковые данные, но будут по прежнему разными контейнерами.
  • значения — структуры, суть которых определяется их содержимым. Два значения с одинаковым содержимым — равнозначны. Как правило изменения их не ожидаемы и даже опасны. Как правило их делают readonly. Передаются значения… по значению :-)

Так вот массив как список конкретных элементов должен быть "значением". В тайпскрипте даже тип для этого есть — ReadonlyArray. А массив, как именованный изменяемый список каких-то элементов — это "контейнер", у него уже есть всякие мутирующие методы.

На этой вашей мутабельность держиться большинство фраемворков(angular), тот же Vue биндит реактивные свойства через замыкание по сути вообще костыль для избежания рекурсии, и таких приемов очень много. Проблема кажется более надуманная, поскольку большинство знают что не примитивные типы передаются по ссылке. Если для вас это действительно проблема Object.defineProperty или typescript вам в помощь.

UFO just landed and posted this here

Ты вообще о чем? Я если что не про "bind call или apply"

UFO just landed and posted this here

Слово "биндинг" и его производные ("биндит", "забиндить") обычно обозначает операцию привязки данных, когда свойства некоторого объекта модели отображаются на свойства или атрибуты элемента UI или наоборот.


К функции Function.prototype.bind этот термин отношения не имеет.

UFO just landed and posted this here

Я про то что вот так примерно Vue следит за изменениями реактивных переменных, вот таким способом они избегают возможную рекурсию:


        props.forEach((prop) => {
            let property = undefined
            Object.defineProperty(this, prop, {
                set: (value) => {
                    let oldValue = this[prop]
                    let newValue = value
                    let name = "set"
                    if (oldValue) {
                        name = "change"
                    }
                    property = value
                    if (this.trigger) {
                        this.trigger({
                            key: prop, name, newValue, oldValue
                        })
                    }

                },
                get: () => {
                    return property
                }
            })
        })
Э… и какие именно строчки тут помогают избежать рекурсии?

2 строчка, потому что внутри сеттера, this[prop] = value юзать нельзя

То есть чтобы избежать рекурсии, мы… не делаем рекурсивного вызова? Я ожидал чего-то более интересного :-)

PS сегодня я избежал форматирования диска своего компьютера (благодаря тому что не стал этого делать), дважды
Странно, что в статье не упомянули spread оператор:
const obj = { foo: 'bar' };
const objCopy = { ...obj };

Он код с его использованием понятен и лаконичен. Пусть он и является синтаксическим сахаром над Object.assign.
Раз упомянули библиотеки, рекомендую обратить внимание на линзы. Они используют ряд популярных техник из мира функционального программирования, чтобы абстрагировать доступ к данным во вложенных структурах. Имутабельность в комплекте. www.npmjs.com/package/partial.lenses.
Конструкция Object.assign позволяет комбинировать два объекта (или большее число объектов), получая на выходе один новый объект

Мутновато изложено в оригинале, и ещё мутнее в переводе. Следовало бы сразу разъяснить, что не какой-то там «новый объект» волшебным образом формируется из свойств аргументов Object.assign, а в первый аргумент копируются значения свойств последующих аргументов, и он же возвращается методом. А то некоторые люди читают до половины, а потом удивляются, откуда у них мутации, «мы же вот тут специально в новый объект всё отправили».
UFO just landed and posted this here

То есть понимать это должны все JS-программисты?

UFO just landed and posted this here
Sign up to leave a comment.