Примитивы в JavaScript — это миф?
- четверг, 27 июня 2024 г. в 00:00:08
Все мы знаем что в JavaScript есть ссылочные (Object), присваивающиеся по ссылке и примитивные типы данных (String, Number, Null и тд), присваивающиеся по значению. Но так ли это на самом деле? В этой статье с помощью небольшого эксперимента мы убедимся, что это не совсем так и посмотрим как "примитивные" типы данных на самом деле хранятся в памяти.
Создадим небольшой HTML файл:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<a class="la1"></a>
<body>
<script>
var word = "word".repeat(1000000);
document.querySelector('.a1').innerHTML = word;
</script>
</body>
</html>
Мы создаем переменную word в которой хранится строка, содержащая 1 миллион повторений слова "word"
Сделаем snapshot во вкладке Memory в браузере Chrome и посмотрим на память
Мы видим что наша огромная строка занимает 4Мб памяти
Далее добавим в наш HTML файл немного кода
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<a class="a1"></a>
<a class="a2"></a>
<body>
<script>
var word = "word".repeat(1000000);
// Добавляем переменную word2
var word2 = word;
document.querySelector('.a1').innerHTML = word;
// Отрисовываем ее значение во вторую ссылку
document.querySelector('.a2').innerHTML = word;
</script>
</body>
</html>
Как мы все много раз слышали, примитивные типы передаются по значению, соответственно в переменную word2 должна попасть полная копия значения, которое лежит в переменной word и составляет 4Мб. В итоге мы ожидаем увидеть увеличение памяти в 2 раза.
Давайте смотреть!
Но как так! Память, занимаемая строками не то что не увеличилась в 2 раза, она вообще не изменилась! И мы не наблюдаем второй строки, переменная word2 ссылается на тот же участок памяти, что и переменная word. Никакой копии не произошло. Попробуем по-другому.
Давайте создадим массив и добавим туда несколько значений
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<a class="a1"></a>
<body>
<script>
var word = "word".repeat(1000000)
var arr = [word, word, word, word, word]
for (let i = 0; i < arr.length - 1; i++) {
document.querySelector('.a1').innerHTML += word;
}
</script>
</body>
</html>
Как мы видим, снова ничего не изменилось. Все 5 элементов массива ссылаются на один и тот же участок памяти.
Мы можем взглянуть на сам массив и увидеть его Shallow Size - объем памяти, непосредственно занимаемый объектом, который не включает память, занимаемую другими объектами, на которые этот объект ссылается.
JavaScript движки оптимизируют использование памяти, храня данные в одном месте и создавая ссылки на одну и ту же строку для всех переменных, которые на неё ссылаются.
Однако, если одна из переменных, ссылающихся на одну и ту же строку, изменяется, движок создаст новую строку, которая будет занимать соответствующий объем памяти.
Это позволяет эффективно использовать память и избегать её ненужного увеличения.