I would say the actual difficulty greatly varies. It is generally easy if you have a good guess about what the code would actually do. It would be much harder if you have nothing to guess, but usually you should have something to start with. Much like debugging, you need a detective mindset to be good at reverse engineering, and name mangling is a relatively easy obstacle to handle in this scale.
Let me give some concrete example from my old comment [1]. The full code in question was as follows, with only whitespaces added:
function smb(){
var a,b,c,d,e,h,l;
return t(function(m){
a=new aj;
b=document.createElement("ytd-player");
try{
document.body.prepend(b)
}catch(p){
return m.return(4)
}
c=function(){
b.parentElement&&b.parentElement.removeChild(b)
};
0<b.getElementsByTagName("div").length?
d=b.getElementsByTagName("div")[0]:
(d=document.createElement("div"),b.appendChild(d));
e=document.createElement("div");
d.appendChild(e);
h=document.createElement("video");
l=new Blob([new Uint8Array([/* snip */])],{type:"video/webm"});
h.src=lc(Mia(l));
h.ontimeupdate=function(){
c();
a.resolve(0)
};
e.appendChild(h);
h.classList.add("html5-main-video");
setTimeout(function(){
e.classList.add("ad-interrupting")
},200);
setTimeout(function(){
c();
a.resolve(1)
},5E3);
return m.return(a.promise)
})
}
Many local variables should be easy to reconstruct: b -> player, c -> removePlayer, d -> playerDiv1, e -> playerDiv2, h -> playerVideo, l -> blob (we don't know which blob it is yet though). We still don't know about non-local names including t, aj, lc, Mia and m, but we are reasonably sure that it builds some DOM tree that looks like `<ytd-player><div></div><div class="ad-interrupting"><video class="html5-main-video"></div></ytd-player>`. We can also infer that `removePlayer` would be some sort of a cleanup function, as it gets eventually called in any possible control flow visible here.
Given that `a.resolve` is the final function to be executed, even later than `removePlayer`, it will be some sort of "returning" function. You will need some information about how async functions are desugared to fully understand that (and also `m.return`), but such information is not strictly necessary here. In fact, you can safely ignore `lc` and `Mia` because it eventually sets `playerVideo.src` and we are not that interested in the exact contents here. (Actually, you will fall into a rabbit hole if you are going to dissect `Mia`. Better to assume first and verify later.)
And from there you can conclude that this function constructs a certain DOM tree, sets some class after 200 ms, and then "returns" 0 if the video "ticks" or 1 on timeout, giving my initial hypothesis. I then hardened my hypothesis by looking at the blob itself, which turned out to be a 3-second-long placeholder video and fits with the supposed timeout of 5 seconds. If it were something else, then I would look further to see what I might have missed.
[1] https://news.ycombinator.com/item?id=38346602