Comments 54
const egg = { name: "Humpty Dumpty" };
egg.isBroken = false;
Такой подход в целом и общем делает код медленнее и часто может приводить к неожиданным последствиям в будущем
Избегая такого код разработка станет дешевле в долгосрочной перспективе
Зачем делать объекты динамическими, когда можно создать основу конфигурации в виде класса, наделив его базовым состояние, а авторам плагинов позволить его расширять?
Когда мы меняем newEgg (подвергаем объект мутации), автоматически меняется и egg. Вы знали об этом?
Я думал, каждый кто изучает джаваскрипт это узнает в первую очередь. Не?
const newEntity = { ...original, ...mutation }
Эквивалентно
const newEntity = Object.assign({}, original, mutation)
developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Operators/Spread_operator
Фразу "объекты в 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;
};
Идея строгой проверки прототипа — убедиться, что на входе лежит именно литерал объекта, а не что-то более сложное.
medium.com/javascript-scene/common-misconceptions-about-inheritance-in-javascript-d5d9bab29b0a
То лучше сразу сказать, что идея универсального deepFreeze заранее обречена на провал.
Камон, если бы не React, который несмотря на название, совсем не реактивный, про иммутабельность никто бы и не вспомнил. И даже React с Mobx не нуждается в иммутабельных структурах.
Не мутации страшны, а неконтролируемые и неотслеживаемые мутации. vuex предупреждает разработчика — «не меняй состояние вручную», а в redux можно запросто это сделать и полдня потом дебажить, «какая сволочь стреляла».
Он такой же как freeze, только позволяет изменения существующим ключам.
Для полноты картины отмечу, что персистентные структуры данных сильно сложнее в написании и отладке, чем свои "рядовые" "братья". В лучшем случае они работают медленнее лишь в константу раз. Но проигрыш в скорости (например я не слышал о персистентном списке работающем за O(1)) может быть на больших объемах данных может быть очень серьезным.
Таким образом, не стоит пихать такие структуры туда, где идет много вычислений.
Вот список как раз — классика персистентных структур. { head: ..., tail: ...}
За O(1) у него работают операции "добавить элемент в начало" и "удалить первый элемент". Итерация по списку делается удалением всех элементов.
Вы привели пример персистентного стека, и он действительно работает за O(1).
Я же имею в виду обычный список типа массива, т.е. чтение/запись в произвольные места.
Лучшее, что мне известно в этой области — персистентные деревья, с запросами за O(log N) и большой константой.
А плюшки, которые дает персистентность (много версий струкутры с возможностью одновременной работы с ними), в типичном веб-приложении вряд ли нужны (например в Redux хранилище единственно, т.о. версия нужна одна).
Object.freeze, Object.assign, ImmutableJS и прочие такие штуки серьёзно так замедляют работу приложения. Я бы рекомендовал воспользоваться TypeScript, который не позволит вам динамически изменить сигнатуру объекта или изменить readonly свойство. При этом ещё на стадии написания кода, а не в рантайме и соответственно без замедления исполнения.
Объекты можно условно разделить на два типа: значения и контейнеры. Беда JS в том, что, например, один и тот же Array выступает и в роли контейнера (push, pop, ...) и в качестве значения (map, filter, ...).
- Никаких опасностей мутыций вы не продемонстрировали. Только описали азы языка и назвали их "опасными мутантами". Кстати, тот самый синий и волосатый монстр, о котором вы говорили, — весьма душевный человек и проницательный собеседник :-)
Java не компилируется в C++.
Любые сущности в программировании можно разделить на 2 типа:
- контейнеры — мы в них можем что-то положить, и что-то вынуть. Контейнер может изменяться, но от этого он не превращается в другой контейнер. Мы можем его куда-то передать в качестве значения и ожидаем, что он будет так же мутабелен. Контейнеры реализуются через ссылочные типы: собственно ссылки, объекты, их поля, просто переменные. Два контейнера могут содержать одинаковые данные, но будут по прежнему разными контейнерами.
- значения — структуры, суть которых определяется их содержимым. Два значения с одинаковым содержимым — равнозначны. Как правило изменения их не ожидаемы и даже опасны. Как правило их делают readonly. Передаются значения… по значению :-)
Так вот массив как список конкретных элементов должен быть "значением". В тайпскрипте даже тип для этого есть — ReadonlyArray. А массив, как именованный изменяемый список каких-то элементов — это "контейнер", у него уже есть всякие мутирующие методы.
На этой вашей мутабельность держиться большинство фраемворков(angular), тот же Vue биндит реактивные свойства через замыкание по сути вообще костыль для избежания рекурсии, и таких приемов очень много. Проблема кажется более надуманная, поскольку большинство знают что не примитивные типы передаются по ссылке. Если для вас это действительно проблема Object.defineProperty или typescript вам в помощь.
Ты вообще о чем? Я если что не про "bind call или apply"
Слово "биндинг" и его производные ("биндит", "забиндить") обычно обозначает операцию привязки данных, когда свойства некоторого объекта модели отображаются на свойства или атрибуты элемента UI или наоборот.
К функции Function.prototype.bind
этот термин отношения не имеет.
Я про то что вот так примерно 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
}
})
})
const obj = { foo: 'bar' };
const objCopy = { ...obj };
Он код с его использованием понятен и лаконичен. Пусть он и является синтаксическим сахаром над Object.assign.
Конструкция Object.assign позволяет комбинировать два объекта (или большее число объектов), получая на выходе один новый объект
Мутновато изложено в оригинале, и ещё мутнее в переводе. Следовало бы сразу разъяснить, что не какой-то там «новый объект» волшебным образом формируется из свойств аргументов Object.assign, а в первый аргумент копируются значения свойств последующих аргументов, и он же возвращается методом. А то некоторые люди читают до половины, а потом удивляются, откуда у них мутации, «мы же вот тут специально в новый объект всё отправили».
JavaScript и ужасы мутаций