"The named handler is executed, but also the entire body-level script is executed. This behavior differs from AppleScript OSA, in which executing a named handler does not run the body of the AppleScript as well."
While JXA has no shortage of defects and omissions, this is correct behavior. It's purely a bug in JXA's [cough]user documentation in not explaining how any of this stuff works to users.
Executing top-level statements is how *nix-y scripting languages (JS, Python, etc) initialize a script's top-level environment; the "implicit run handler" concept is unique to AppleScript's parser. Therefore a JS script will *always* execute when "compiling" it into a persistent OSA script instance**; any statements that should not be evaluated until the script is "run" must be put in an explicit "run" handler instead. If you have an interest in pathology, feel free to spelunk my old JavaScriptOSA reference implementation that I sent to Sal back when they still had a chance to get it right (hxxps://sourceforge.net/projects/appscript/files/). It was a quick-n-dirty hack thrown together in a few weeks so ain't pretty or bug-free, but it implements almost all of the OSA API along with client-side Apple event support.
That all said, seeing how JXA was such a disaster it not only failed to reboot the Mac Automation platform but destroyed Apple's Automation team and thereby knocked the last nails into AppleScript's coffin as well, I really wouldn't waste another second on it if I were you. Just recommend your user to stick to AppleScript like everyone else: it's the only OSA language that has ever worked right, and ever will.†
--
** JXA does screw up OSA persistency in other ways, mind, which is why, for [sic]instance, the following 'counting' script doesn't work right in Script Editor:
var i = 0; function run() { i++; return i }
, yielding 1,1,1,1,… instead of 1,2,3,4,….
† (At least until developer and user abandonment and maintenance-mode bitrot finally does for it as well.)