Ночной бред



Хотел вам показать интересный пример, что иногда
JSON.parse(JSON.stringify(obj))
работает быстрее, чем
{...obj}
. Собираем простой тест с большим циклом, загоняем полученные клоны в параметры функции и готово:



test(JSON.parse(JSON.stringify(objToCopy)))
значительно быстрее, чем
test2({...objToCopy})




Почему? кажется, что ответ на поверхности: запускаем Ноду с --allow-natives-syntax и проверяем





const a = {a: 1};

const b = {...a};

%HaveSameMap(a, b); // false



const c = JSON.parse(JSON.stringify(a));

%HaveSameMap(a, c); // true





Дело раскрыто, функция стала мегаморфной, JIT вырубился, можно писать пост.



Но! Попробуем добавить вариант с
test3(structuredClone(objToCopy))






test with JSON.parse: 268 ms.

test with structuredClone: 308 ms.

test with spread: 2802 ms.





Проверяем:





const d = structuredClone(a);

%HaveSameMap(a, d); // false





Таааак



Что же получается. И spread и structuredClone ведут к мегаморфности. Но при этом structuredClone не приводит к деоптимизациям.

Дальше докидываю
Object.assign({}, objToCopy)
и не смотря на мегаморфность





test with Object.assign: 238 ms.





Психую. Запускаю с --trace_deopt и вижу, что спред постоянно вышибает
(kind: deopt-eager, reason: wrong map)




Но почему Object.assign и structuredClone не приводят к деоптимизации? Я не знаю 🤡 и могу только предположить, что срабатывает какая-то дополнительная эвристика, и несмотря на то, что мапы разные, в деоптимизацию JIT здесь не уходит. (потому что надо спать!) А спред под такую эвристику не попадает (или, точнее, попадает через раз, так как чем больше попыток вызова, тем чаще случается деоптимизация).