Open utterances-bot opened 3 years ago
Excellent article! I never exactly understood why objects were by reference, but why the properties inside (which I now know are primitive values) were not.
Do you have any articles on object destructuring?
EX:
let test = { name: "John" };
let test1 = { ...test }
Is javascript deferencing test
in this example to create a new object?
@LeeRob97 thanks a lot! I've had a hard time figuring things like these out because this information is scattered across the internet.
Destructuring objects is the same as calling Object.assign({}, test)
, which creates a new object in the heap.
So in your example, test1
would hold a reference to a new object. test
would still point to the same object.
If test
had nested objects though, both objects test
and test1
would point to the same nested object. That's because the spread operator and Object.assign only copy the values, like primitive values or references, not the object these values point to (that's called a shallow copy):
e.g:
const test = {
name: 'John',
dog: {
name: 'Puppy',
}
}
const test1 = { ...test }
test
and test1
will point to the same dog
object.
Great article ! However i'm a bit confused by the use of stacks and heap. Mind you, i've been studying memory management at the OS level. I know what it's about, yet I feel like i'm missing something. The OS handles static and dynamic data into two areas, namely the Stack and the Heap. They grow in opposite directions. The Heap is named that way because all in all, it has no order whatsoever, hence the importance of keeping reference and manage freeing it. The Stack is named that way because it is a specific type of data-structure that goes LIFO, so if you want to get the 7th item from the top of the stack, you'll have to take out the 6 firsts and eventually put them back. But dealing with a lot of objects and arrays which contains primitive values would make the use of stack very slow since a lot of values would have to be popped off to fetch, say, the 30th or 40th item.
I think i've come through this question and had an answer, but i can't quite recall. Either there's actually optimized ways to walk through the stack, or there's actually a single name for 2 things : the Stack as an area for memory that is managed however the interpreter/OS wants to, and the Stack as a data-structure that the OS uses to manage said memory.
A refresher would be welcome if you have an idea.
Hey @rewieer, thanks for the feedback!
You're right, the stack and the heap have different ways of storing data.
Regarding the stack being LIFO: I believe that by this, people mean the order in which the functions will be processed, which is why it's also referred to as the call stack. The engine would execute the function on top of the stack first. I wrote about this in the first post of this series.
I found a stackoverflow answer where they explained which algorithms are used for storing data in the stack, but I can't find it anymore. If I find it I'll let you know, it basically said which algorithm is used to save and access the data in the "stack".
But yes, you're right and I think it's confusing to have the same name for multiple things: the call stack, the stack where JS saves primitive values, and the stack data-structure.
This is a fantastic article. Thank you!
@dwalker11 thanks :)
Great article one thing I would add is that in javascript if you want automatically trigger garbage collection on an array you can set the length to 0. That would probably be easier and more effective than doing the splice in your example.
Are you sure about stacks being static. As far as I remember from other languages, when calling methods, method frames are pushed onto stack, including it's scoped variables and all.
Would be much helpful for others if you can summarize https://developers.google.com/web/tools/chrome-devtools/memory-problems in short.
@mlavina, thanks, you're right. I will update the article accordingly :)
@Niproblema, yes, it's static, which means that all values in the stack get assigned a fixed amount of memory, regardless of whether they will use all of it or not.
I think what you're referring to is the data structure of the stack. In the first article , I explain how this works more in detail. But you're right; the stack basically behaves as you described it.
@sandeshdamkondwar, I'll have a look. Maybe this would be a fit for another article :)
thanks I think it would also be worth mentioning weakmap, weakset
@VladyslavGoloshchapov thanks for pointing that out. I wasn't aware of this memory leak; I'll look more into it and add it to the article :)
these aren`t memory leaks. quite the opposite
Right, they solve memory leak issues with JS maps. I was looking at this stack overflow answer, which seems to explain it pretty well.
Thank you for providing me with a fantastic article. Can I post it on my blog in my mother tongue?
@jskim-cian, sorry, I somehow missed your comment.
Sure go ahead :) Please send me the link when you're finished, so I can check it out
Awesome, thanks! Can you please also link back to the article?
Sorry. I saw it now. 😢 I wrote it down on the top, but can't you see it?
@JSKim-Cian no worries haha thanks again for the translation
The 2-article series is great! It opened up my mind to JS engine a lot more. I have a question about reassign a new primitive value to a variable which is declared and assigned on above code. The JS interpreter will push new value to that "layer" in Stack or remove that layer and push a new layer of that variable with new value in?
I love it
Your last example snippet, which I copied below, will not delete all the DOM elements in the elements
array, since you're directly mutating the elements
array while iterating through it.
For each item you delete from elements
using Array.prototype.splice
, the order of the remaining items in elements
will change, as they will be shifted to the left each time an item is deleted.
Here's an example to demonstrate it:
let items = [1,2,3,4,5,6,7,8,9];
function deleteItems() {
items.forEach((item, index) => {
items.splice(index, 1)
})
}
// you can see here that not all of the elements were deleted
items // [2,4,6,8]
Here's what happened at the first 2 iterations, which will help explain the output above
Iteration 1:
index == 0
you delete the item at index 0(number 1), this will result in the items
array being mutated to [2, 3, 4, 5, 6, 7, 8, 9]
now the element that was at index 1
namely the number 2, is shifted left to take index position 0.
Iteration 2:
Index == 1, at this point index == 1
is not number 2
because of the direct deletion you performed at iteration 1
Now index 1 points to 3
, which gets deleted and as a result items
array will look like [2, 4, 5, 6, 7, 8, 9]
// further iterations: same concept as the above 2 iterations above
The result is that every other item got deleted, but not all of them
Solutions:
elements
in your last example after you're done iterating over the elements.This is the code example you shared
const elements = [];
const element = document.getElementById('button');
elements.push(element);
function removeAllElements() {
elements.forEach((item, index) => {
document.body.removeChild(document.getElementById(item.id));
elements.splice(index, 1);
});
}
Solution 1: dereferencing the elements
array after you're done iterating over it
const elements = [];
const element = document.getElementById('button');
elements.push(element);
function removeAllElements() {
elements.forEach((item, index) => {
document.body.removeChild(document.getElementById(item.id));
});
elements = null;
}
Solution 2: delete each element after you're done with until there are no more items left in the array
const elements = [];
const element = document.getElementById('button');
elements.push(element);
function removeAllElements() {
let index = 0;
while (elements.length !== 0) {
let item = elements[index];
document.body.removeChild(document.getElementById(item.id));
elements.splice(index, 1);
}
}
@fgerschau other than that, I'd say that the article is an excellent resource. I had bookmarked it for future reference.
@fgerschau I tried to subscribe to your newsletter using the bottom subscribe box on your homepage, and it resulted in an error.
Oops! An Error Occurred The server returned a "405 Method Not Allowed". Something is broken. Please let us know what you were doing when this error occurred. We will fix it as soon as possible. Sorry for any inconvenience caused.
I looked at the requests made from the network tab under chrome's developer tools, and it looks like you forgot to set the correct request method on your homepage's bottom subscribe form. As a result, when I submitted my email from it, it was sent as a GET
request by default and not a POST
request, which it should be.
I hope that helps!
Excellent article, Mark nd Sweep thats the core.
It's Great article! I learned much about JS Memory Manage. And I want let more reader read about this artice. So Can I repost and translate your article to the Chinese technical forum ?😀
@daiwanxing sure! Please send me the link when you're done
@daiwanxing sure! Please send me the link when you're done
no problem. Finally I have done, This is the translated article link: https://juejin.cn/post/7200956719978790967
Thanks, @daiwanxing! I added the links to your and @CianJS's translation to the article.
Thanks for your article. This article helped me to understand memory in the JS engine. But i don't understand why, in the figure describing the Mark-and-sweep algorithm, the object 'dog' in the memory heap is considered a garbage even though it is still referenced by the object 'dog' in the memory stack?
JavaScript's Memory Management Explained
Even though the JavaScript engine manages memory for us, it's good to know what happens under the hood.
https://felixgerschau.com/javascript-memory-management/?ck_subscriber_id=478674400