Насколько хорошо вы знаете JavaScript?
Как в JS передаются значения - по ссылке или по значению? Не спешите, подумайте даже если вы "мидл" или синьор-помидор.
Можете в опросе ответить:
Как в JS передаются значения - по ссылке или по значению? Не спешите, подумайте даже если вы "мидл" или синьор-помидор.
let str1 = 'Famabara'; let str2 = str1; // Тут копия или новая строка? let obj1 = { name: 'Famabara' }; let obj2 = obj1; // Тут копия или новый объект?
Можете в опросе ответить:
Как передаются значения при присвоении в переменную или в свойство объекта?
Теперь посмотрим, что нам говорит learn.javascript.ru - очень популярный в рунете учебник JavaScript:
А теперь давайте запустим в NodeJS такой код:
После запуска смотриим программой top потребление ресурсов:
Теперь изменим немного код:
Смотрим ещё раз через top:
Программа потребила всего 73 Мб вместо 1 Гб. Согласно популярной теории о копировании примитивов такого быть не должно. ;)
В чем разница между двумя примерами кода? В первом случае мы всякий раз создаём новую строку, а а во втором - пихаем в массив ту же самую строку из созданной переменной. Если вы вдруг подумали, что во втором случае надо предварительно поместить значение в переменную-посредник, а потом уже пушить в массив, то нет - это не поможет, оперативка жраться всё равно не будет.
Кстати, на ноде в Windows 10 аналогичное поведение - около 1 Гб потребление против менее 100.
Ну, а теперь главный секрет! :)
В JavaScript всё передаётся/копируется по ссылке!
const arr = []; for (let i = 0; i < 1200200; i++) { arr.push('0123456789'.repeat(1000 * 1000)); } while (true) { }
После запуска смотриим программой top потребление ресурсов:
39858 dima 20 0 2026180 1,0g 37824 R 100,0 3,2 0:21.44 nodeНа моей Кубунте сожрано 1 Гб оперативки. Ух ты.
Теперь изменим немного код:
const arr = []; const text = '0123456789'.repeat(1000 * 1000); for (let i = 0; i < 1200200; i++) { arr.push(text); } while (true) { }
Смотрим ещё раз через top:
40267 dima 20 0 1066148 73796 37440 R 99,7 0,2 0:12.27 node
Программа потребила всего 73 Мб вместо 1 Гб. Согласно популярной теории о копировании примитивов такого быть не должно. ;)
В чем разница между двумя примерами кода? В первом случае мы всякий раз создаём новую строку, а а во втором - пихаем в массив ту же самую строку из созданной переменной. Если вы вдруг подумали, что во втором случае надо предварительно поместить значение в переменную-посредник, а потом уже пушить в массив, то нет - это не поможет, оперативка жраться всё равно не будет.
Кстати, на ноде в Windows 10 аналогичное поведение - около 1 Гб потребление против менее 100.
Ну, а теперь главный секрет! :)
В JavaScript всё передаётся/копируется по ссылке!
Вообще, все эти "передача по ссылке" и "передача по значению" - не совсем корректные термины.
Увы и ах, но в JavaScript есть только один источник правды - это спецификация, а она в JS просто отвратительная по множеству причин.
Но давайте попробуем к ней обратиться.
В ECMAScript 2023 пункте 5.2 написано:
Т.е. сказано, что по умолчанию передача по ссылке (reference-like behaviour) и только при прямом указании в спецификации делается копия, причём неглубокая (shallow copy of someValue).
Попытаюсь объяснить поведение хост-среды JS:
С подобным поведением движку JS даже проще проводить сравнение. Если с двух сторон у нас одна ссылка, то строки не нужно побайтово сравнивать, можно сразу вернуть true.
Увы и ах, но в JavaScript есть только один источник правды - это спецификация, а она в JS просто отвратительная по множеству причин.
Но давайте попробуем к ней обратиться.
В ECMAScript 2023 пункте 5.2 написано:
Algorithm steps may declare named aliases for any value using the form “Let x be someValue”. These aliases are reference-like in that both x and someValue refer to the same underlying data and modifications to either are visible to both. Algorithm steps that want to avoid this reference-like behaviour should explicitly make a copy of the right-hand side: “Let x be a copy of someValue” creates a shallow copy of someValue.
Т.е. сказано, что по умолчанию передача по ссылке (reference-like behaviour) и только при прямом указании в спецификации делается копия, причём неглубокая (shallow copy of someValue).
Попытаюсь объяснить поведение хост-среды JS:
// Выражание справа от оператора присваивания создало // ссылку на строку и вернуло её. // Это ссылка поместилась в переменную str1. let str1 = 'Famabara'; // Выражание справа от оператора присваивания вернуло ту же ссылку на строку. // Это ссылка поместилась в переменную str2. let str2 = str1; // Выражание справа от оператора присваивания // вернуло новую ссылку на строку. // Это новая ссылка поместилась в переменную str1. // А переменная str2 содержит "старую" ссылку на строку. str1 = 'Famabara super';
С подобным поведением движку JS даже проще проводить сравнение. Если с двух сторон у нас одна ссылка, то строки не нужно побайтово сравнивать, можно сразу вернуть true.
Можно ещё на bun погонять