Pull to refresh

Comments 76

Пункт 2
const obj = {a:1};
const myObjArray = [obj, obj];
const deDupeObj = [...new Set(myObjArray)];
const myArray = [{a:1}, {a:1}];
const deDupe = [...new Set(myArray)];
// Как и ожидалось
console.log(deDupeObj); // [{a:1}]
// Упс...
console.log(deDupe); // [{a:1}, {a:1}]

Эту ситуацию надо объяснить, раз уж взялись давать советы.
Поведение ожидаемое и очевидное, что здесь объяснять?
Для кого оно ожидаемое и очевидное, тот не почерпнет в статье для себя ничего нового. Ну а впрочем это только моё мнение…
то, как передаются объекты (по ссылке) — это первые главы любого учебника по JS

ссылки — это азы, их в учебниках объясняют, а не в статьях, которые рассчитаны на людей, уже во всю херачащих на JS.


Для кого оно ожидаемое и очевидное, тот не почерпнет в статье для себя ничего нового.

такие вещи, как обмен значений 2х переменных — вещь неочевидная даже для человека, который знает о destructuring assignment.

+1, не приходило в голову, что destructuring assignment может работать и без декларации (const, var, let). Плюс я был почти уверен, что { ...myProperty && { propName: myProperty } } просто упадёт с ошибкой если myProperty будет non-object (не падает).

Я это знал, но удивился, что деструктуризация работает и в следующем случае:
function swap(array, index1, index2){
  [array[index1], array[index2]] = [array[index2], array[index1]];
}
UFO just landed and posted this here
Да мне то Вы зачем это объясняете? Я прекрасно это знаю. Коммент о том, что тот, кому будет полезна статья, может не знать этих нюансов.
Сужу по вопросам на Тостере и в чатиках Телеграма. Частенько проскакивают попытки сделать массив уникальных объектов с помощью Set.
UFO just landed and posted this here
13: На самом деле такое копирование обладает рядом недостатков.

Эти вот «недостатки» — они не обязательно недостатки, иногда они наоборот крайне полезны. Единственное, что тут нужно иметь в виду, что прогон объектов через JSON — это всё-таки НЕ клонирование.

И, кстати, оно очень даже быстро работает; делая клонирование через обход дерева объектов руками, вполне можно сделать даже медленнее.
Если понадобилось копировать сложный объект, скорее всего где то выше в архитектуре проблемы.
Ну щас, ага.

У вас есть стейт приложения. Пользователь в этот стейт вносит изменения, которые должны будут примениться по нажатии кнопки «применить изменения». Как будете хранить «грязный» стейт? Как дельту к чистому? И если никакие undo-redo заведомо не нужны — всё равно будете дельты хранить? И будете писать код по созданию дельт и их накатыванию, вместо того, чтоб отклонировать чистый стейт, а потом залить грязный поверх чистого (в одну строчку)?
Угу. 64Кб жаваскрипта. Повторить двадцать раз на любой чих, а потом удивляться, чё это у нас ничего особенного не делающие страницы грузят мегабайты скриптов.

И к слову о этой ветке комментариев, Immutable.js — это как раз таки копирование объектов во все поля. Только зашитое в либу.
А вот и нет. Там хитрые внутренние механизмы, избегающие дублирования данных и лишней работы.
Там хитрые внутренние механизмы, избегающие дублирования данных и лишней работы.

Никакая внутренняя хитрость не поможет вам сохранить два указателя на данные по той же цене, что и один указатель. Я в курсе, что immutable.js очень неплохо оптимизирован (и вообще либа концептуально хорошая), но то, что он делает — это всё равно копирование структур данных. Даже если копируется только левая сторона (указатели).

Дык это ж по четыре байта на указатель, мало же!

Immutable.js
Этот тот, в котором поля указываются строками и IDE никак не может помочь с написанием кода? «Гениальное» решение.

Люди уже давно на TS переходят, а некоторые — до сих пор в динамику бросаются
Это проблема IDE, а не библиотеки.
Ну да, проблема абсолютно всех IDE, которые не поддерживают одну очевидно динамическую библиотеку, а не какой-то одной кривой динамической библиотеки.
Да уж, я вчера был очень пьяный и написал очень глупую вещь. Я пытаюсь реконструировать, как пришёл к этой мысли, но не получается.
Все мы иногда по пьяни пишем на Хабру

Решал подобную задачу. Активно использовал иммутабельность (без immutable.js). В итоге для redo/undo не хранил никаких дельт, а хранил ссылку на предыдущий стейт целиком. Сами понимаете, вес одного undo-среза был крошечный.


Вопрос с сериализацией и десериализацией решил просто: кастомный cloneObject метод, который используя Map упаковывал state в json, где все circular references были строкой вида $circular$firstPath. И был такой же метод десериализации который строил такой же объект восстанавливая все ссылки. На всё про всё строк 30 кода.

UFO just landed and posted this here
Вот прям уронит? Мне кажется, все быстрые алгоритмы сортировки должны такой компаратор нормально переварить, потому что у них тупо нет времени, чтобы наткнуться на неконсистентность. Потому что неконсистентность — это когда мы получили результат сравнения, противоречащий предыдущим результатам. Но если он противоречит им — значит, мы могли его знать ещё до того, как провели сравнение (предполагая, что компаратор «нормальный»).
UFO just landed and posted this here
Вредный «однострочник»:
myArray.sort(() => { return Math.random() - 0.5});


Если хочется использовать Math.random(), то в одну строку примерно так
arr = arr.map(a => ({sort: Math.random(), value: a})).sort((a, b) => a.sort - b.sort).map(a => a.value)


Детали: stackoverflow.com/questions/962802/is-it-correct-to-use-javascript-array-sort-method-for-shuffling
Давайте лучше полезные однострочники на Perl
Или на Питоне. Мой любимый, квиксорт в одну строку:
qs = lambda L: [] if L==[] else qs([x for x in L[1:] if x<L[0]]) + L[0:1] + qs([x for x in L[1:] if x>=L[0]])
Не очень хороший выбор pivot. Должен плохо работать для почти отсортированных массивов.
Зато нагляднее некуда.
Да, тут скорее для наглядности или искусства ради. У него есть проблема есть и посерьёзнее, будучи рекурсивным не будет работать для списков больше определённого размера. Причём не очень большого, порядка тысячи.
Аж даже любопытно стало вспомнить перл и написать то же самое на нём =)
$qs = sub { my ($car, @cdr) = @_; @_ == () ? () : ($qs->(grep { $_ < $car} @cdr), $_[0], $qs->(grep { $_ >= $car} @cdr)) };
Ну и грех было бы не перевести на javascript, раз уж это тема статьи:

const qs = a=>a.length?[...qs(a.slice(1).filter(e=>e<a[0])),a[0],...qs(a.slice(1).filter(e=>e>=a[0]))]:[];
UFO just landed and posted this here
JSON.parse(JSON.stringify(originalObject));
Удачи в копировании объектов с properties NaN, Infinity, Function, и многими другими.
Если честно, не люблю эти однострочники, некоторые из них просто щекочат эго джунов, не принося никакой пользы. Да, их надо знать, чтобы распознавать в чужом коде, но свой код должен быть кристально ясен.
Если вы удаляете дубликаты из массива, как насчет функции `removeDuplicates(arr, predicate)`? Вы можете реализовать ее оптимальным способом, она будет фурычить быстрее, а постороннему программисту будет сразу ясно — вот тут удаляют дубликаты. Ну или в каком-нибудь lodash такая функция наверняка есть. Вы можете гарантировать, что ваш код всегда будут читать любители и знатоки однострочников? Или может быть, в вашем проекте появляются иногда джуны, или переученные за две недели с джавы «фуллстэк-девелоперы»? Сколько времени это будет стоить вашей компании на промежутке в хотя бы год? Если ваша зарплата включает в себя процент от прибылей, такие вопросы — не праздные.

Мой вывод: такие вещи могут быть полезны только в случае если вы пишете код на выброс, для себя на один раз. Как только речь о серьезных продуктах и мало-мальских командах, такое ковбойство надо вымарывать.
Или вот пункт 8 — ну спотыкается взгляд о такие конструкции. А что если в объекте справа переименовалось свойство, а у вас нет тайпскрипта и приличной IDE, которая бы все подчеркнуло?
Пункт 13 — очень плохо давать такие советы. Во-первых, автор просто так ляпнул, что этот способ медленный. Он не медленный, он самый быстрый.
Во-вторых, применять этот способ нужно не когда у вас мало времени, а когда вы точно уверены, что объекты будут простенькие, без циклических ссылок или ссылок на какие-нибудь гигантские другие объекты и нужно чтобы это было супер-быстро, потому что делается миллиарды раз. Да я могу маленький движок графовой БД таким, что попытка `stringify` одного из узлов выведет полностью всю эту базу. Первый вопрос, который надо задавать человеку, делающем deepCopy — «а без deepCopy точно ничего не получится»?

Он не медленный, он самый быстрый.

В каком месте он самый быстрый? Я прошелся по тесту в Вашей ссылке, и мне показоло что он самый медленный, при этом в два раза медленее того же 'Object.assign({}, obj);'
дык а Object.assign делает shallow copy, не deep copy
Согласен, но собственно способ 'JSON.parse(JSON.stringify(obj))' копирует очень малую часть от самого объекта, так что я бы не стал их сравнивать, так как одно и другое далеко от полного копирования объекта.
Хахаха, а вот тут уже я, не подумав, ляпнул. И впрямь, самый медленный, на jsbench 100% — это максимум, чем меньше процентов тем лучше. Как удалять комменты на хабре
Пункт 12 — так выглядит код говнокодера и script-kiddy, не понимающего ни что такое инкапсуляция, ни что такое предусловия. Ты взял неизвестный тебе алгоритм сортировки, который ожидает консистентную функцию сравнения (если A>B, то B<A, а если A>B и B>C, то A>C) и передал туда фигню вместо консистентной функции сравнения. Как будет работать неизвестный тебе алгоритм, если передать в него не то, что он ожидает? Завершится ли он когда-нибудь? Если завершится, то за какое время? Ах, «но ведь оно работает, я пробовал во всех браузерах?»

P.S. это не обращение к авторам статьи, это просто моя клокочущая ненависть к таким идеям.
> Приведение значений к логическому типу
> myBoolean = !!myVariable;

А зачем?
Ведь, если myVariable = 1, то
if ( myVariable ) {
    // я и без boolean, а на честном <s>слове</s> integer попаду в эту ветвь оператора if
}
Вот тоже не знаю зачем это делать, разве что при проверке ===, хотя тоже не понятно зачем это делать, помню даже когда то спорил с одним 'специалистом', который с пеной у рта доказывал что нужно писать именно 'if (!!myVariable)', хотя так и не смог привести доказательства, когда данный пример будет отличаться от 'if (myVariable)'
Так делать нужно, очевидно, когда вам нужен именно boolean (для дальнейших вычислений). Использовать это прямо в ифах — довольно бессмысленная затея, а вот «утечь» строку в код, который ожидает именно boolean (скажем, в сериализацию) — опасно.
Полностью с Вами согласен, я лишь приводил пример только про ифы.
Чаще всего подобное приведение к boolean встречается в конструкциях типа switch(true), т.к. switch как раз сравнивает строго:
const myVariable = 1;
switch (true) {
    case myVariable:
       console.log('Эта ветка не отработает');
       break;
    
    case !!myVariable:
        console.log('А эта отрабтает');
        break;
}
Как я уже писал выше, это было сказано лишь для ифа, а для строгого сравнения — да, можно такое пременить.

Но чисто для интереса хотело бы спросить, где вообще такой свитч можно применить? Не лучше для данного примера использовать обычный if/else?

Такой switch можно использовать если очень хочется не пройти code-review :)

Для данного конкретного, естественно, if'а достаточно. Даже просто && обойтись можно. Это была просто демонстрация того, что switch требует строгого равенства, и если об этом забыть, можно получить неожиданный результат.

А сам switch(true) именно с необходимостью приводить к boolean я нередко наблюдал в ситуациях, подобных вот этой:
getSomeMeasurementAsync((err, data) => {
    switch (true) {
        case !!err:
            handleError(`Error getting measurement`, err);
            break;

        case !!data.error:
            handleError(`Measurement error`, data.error);
            break;

        default:
            handleMeasurement(data.measurement);
    }
});

Задумка конечно интересная, запомню, может когда нибудь пригодиться, но даже в данном случае я бы не рекомендовал использовать данную структуру.
Чем тут обосновано использование switch'a и '!!'? Чисто «выпендриться»?
getSomeMeasurementAsync((err, data) => {
  if (err) {
    return handleError(`Error getting measurement`, err)
  } else if (data.error) {
    return handleError(`Measurement error`, data.error)
  }
  return handleMeasurement(data.measurement)
})
Без else после return будет ещё чище.

!!variable очень распространённый "хак". Используется когда вам нужно гарантировано привести к boolean-у и не хочется писать variable ? true : false. Т.е. не в if(), until(), repeat() и пр., а скорее при формировании нового объекта где по контракту должен быть boolean.

Кажется я в третий раз буду писать про это :)
Я знаю про этот «хак», и тоже использую его временами, и как написали Вы, и написали выше, он очень хорошо подходит для строгого сравнения, либо же при сериализации, но я лишь писал про ифы, не более того :)

Есть даже линтеры чтобы if(!!...) не использовали :-)

Вообще я не понимаю от куда пошла мода писать if(!!...), так как где то два года назад, много кто мне об этом начал говорить, хотя выводов так и не предоставляли, лишь в стиле «так надо», как будто кто то, что то ляпнул, и все как стадо начали повторять…

Ну а если у вас какой-нить filter, и вы не знаете, что у него внутри — строгое или нестрогое сравнение?

UFO just landed and posted this here
const myObject = { ...myProperty && { propName: myProperty } }

Да ну, такое надо на ревью обратно отправлять.
UFO just landed and posted this here

А почему бы не просто "myProperty ? { propName: myProperty } : {}"?


Вроде и понятнее, и символов меньше...

UFO just landed and posted this here
А как было бы лучше? if'ами?

Да, лучше if'ами
let config = {
  value1,
  value2,
}
if (condition1) {
  config = { ...config, value3, value4 }
}
...
А как было бы лучше? if'ами?
А ещё лучше, чтобы у вас был строго-типизированный код и наполнение объектов полями не зависело бы от if'ов
Пункт 10. Быстрое создание числовых массивов
На MDN есть еще вот такой пример:
// Генерирования последовательности чисел
Array.from({ length: 5 }, (v, k) => k); 
// [0, 1, 2, 3, 4]
А разве однострочные конструкции не затрудняют чтение кода в больших объёмах?

Всегда казалось, что например написать `const myBoolean = Boolean(myVariable);` более очевидно для понимания, нежели играться с восклицательными знаками. Впрочем это актуально для любого приведения типа, будь то строка или число.
По-моему вот так
const code = Math.floor(Math.random() * Math.pow(10,6)).toString().padStart(6, "0");

Будет чище. Не придется считать нули :)
Sign up to leave a comment.