Как стать автором
Обновить

Комментарии 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]];
}
НЛО прилетело и опубликовало эту надпись здесь
Да мне то Вы зачем это объясняете? Я прекрасно это знаю. Коммент о том, что тот, кому будет полезна статья, может не знать этих нюансов.
Сужу по вопросам на Тостере и в чатиках Телеграма. Частенько проскакивают попытки сделать массив уникальных объектов с помощью Set.
НЛО прилетело и опубликовало эту надпись здесь
13: На самом деле такое копирование обладает рядом недостатков.

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

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

У вас есть стейт приложения. Пользователь в этот стейт вносит изменения, которые должны будут примениться по нажатии кнопки «применить изменения». Как будете хранить «грязный» стейт? Как дельту к чистому? И если никакие undo-redo заведомо не нужны — всё равно будете дельты хранить? И будете писать код по созданию дельт и их накатыванию, вместо того, чтоб отклонировать чистый стейт, а потом залить грязный поверх чистого (в одну строчку)?
Immutable.js
Угу. 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 кода.

НЛО прилетело и опубликовало эту надпись здесь
Вот прям уронит? Мне кажется, все быстрые алгоритмы сортировки должны такой компаратор нормально переварить, потому что у них тупо нет времени, чтобы наткнуться на неконсистентность. Потому что неконсистентность — это когда мы получили результат сравнения, противоречащий предыдущим результатам. Но если он противоречит им — значит, мы могли его знать ещё до того, как провели сравнение (предполагая, что компаратор «нормальный»).
НЛО прилетело и опубликовало эту надпись здесь
Вредный «однострочник»:
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]))]:[];
НЛО прилетело и опубликовало эту надпись здесь
Если честно, не люблю эти однострочники, некоторые из них просто щекочат эго джунов, не принося никакой пользы. Да, их надо знать, чтобы распознавать в чужом коде, но свой код должен быть кристально ясен.
Если вы удаляете дубликаты из массива, как насчет функции `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, и вы не знаете, что у него внутри — строгое или нестрогое сравнение?

У filter внутри сравнение такое, которое вы ему туда положите. RTFM -> «Falsy/truthy values»
НЛО прилетело и опубликовало эту надпись здесь
const myObject = { ...myProperty && { propName: myProperty } }

Да ну, такое надо на ревью обратно отправлять.
НЛО прилетело и опубликовало эту надпись здесь

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


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

НЛО прилетело и опубликовало эту надпись здесь
А как было бы лучше? 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");

Будет чище. Не придется считать нули :)
Зарегистрируйтесь на Хабре, чтобы оставить комментарий