You are reading a single comment by @allObjects and its replies. Click here to read the full conversation.
  • @Gordon,

    So if that if doesn't get run in Espruino, no local var is created and it assumes that the var is global, overwriting c

    I assume this is because Espruino is interpreting on the source code without compilation into any other format first, vs practically all and especially the browser JS engines do.

    Solution of the issue would be to declare the variable as 1st thing in the function body before any use. I'm sure this would also survive the minifier and cost 'nothing':

    Code_1:

    move : function(dir) {
          var item,a;
          if (l.selectEdit) {
            item = l.selectEdit;
            item.value -= (dir||1)*(item.step||1);
            if (item.min!==undefined && item.value<item.min) item.value = item.min;
            if (item.max!==undefined && item.value>item.max) item.value = item.max;
            if (item.onchange) item.onchange(item.value);
            l.draw(options.selected,options.selected­);
          } else {
            a=options.selected;
            options.selected = (dir+options.selected)%menuItems.length;­
            if (options.selected<0) options.selected += menuItems.length;
            l.draw(Math.min(a,options.selected), Math.max(a,options.selected));
          }
        }
    

    I could go even a step further and actually gain some bytes by doing some funny things:

    Code_2:

    move : function(dir,item,a) {
          if (item = l.selectEdit) {
            item.value -= (dir||1)*(item.step||1);
            if (item.min!==undefined && item.value<item.min) item.value = item.min;
            if (item.max!==undefined && item.value>item.max) item.value = item.max;
            if (item.onchange) item.onchange(item.value);
            l.draw(options.selected,options.selected­);
          } else {
            a=options.selected;
            options.selected = (dir+( a=options.selected))%menuItems.length;
            if (options.selected<0) options.selected += menuItems.length;
            l.draw(Math.min(a,options.selected), Math.max(a,options.selected));
          }
        }
    

    Note that the Espruino IDE editor's linter complains with a warning about the logical expression in 1st line of function body to be an unexpected assignment. But that is just the linter. Since I do not like warnings hanging around, I use double parens to 'hide' the fact: ((item = l.selectEdit)). Adding the local var definitions to the function's argument list does get them defined for me... (and since there are no optional args in move() nor there is some arg count dependency, it does not hurt).

    Looking even closer at what is going on, I notice that actually only exactly one var is needed that is mutually exclusively used in the then and else path to hold on to a value. Therefore, this is my - almost - final solution (legible? ...that's a different ball game).

    Code_3:

    move : function(dir,v) {
          if (v = l.selectEdit) {
            v.value -= (dir||1)*(v.step||1);
            if (v.min!==undefined && v.value<v.min) v.value = v.min;
            if (v.max!==undefined && v.value>v.max) v.value = v.max;
            if (v.onchange) v.onchange(v.value);
            l.draw(v=options.selected,v);
          } else {
            options.selected = (dir+(v=options.selected))%menuItems.len­gth;
            if (options.selected<0) options.selected += menuItems.length;
            l.draw(Math.min(v,options.selected), Math.max(v,options.selected));
          }
        }
    

    Growing up with <= 16KBytes for serious biz application including utilities, such as ISAM for one key, you end up really at the bottom of things: since item.value and options.selected shows up so many times, getting a second work variable w in will cut back even more on footprint (and legibility/maintainability - :\ ), and(/but) surpasses what ever a minifier can do without knowing that there is no compiler with optimization involved on execution:

    Code_4:

    move: function(dir,v,w) {
      if (v = l.selectEdit) { // --- value selection
        w = v.value - (dir||1)*(v.step||1);
        v.value = w = (v.min!==undefined && w<v.min) ? v.min
                    : (v.max!==undefined && w>v.max) ? v.max
                    : w;
        if (v.onchange) v.onchange(w);
        l.draw(w=options.selected,w);
      } else { // --- menu item selection
        w=((dir||1)+(v=options.selected)+(w=menu­Items.length))%w;
        l.draw(Math.min(v,options.selected=w), Math.max(v,w));
      }
    }
    

    I'm surprised that item.min and item.max can be undefined; after all it looks like a magnitude thing and not discrete (non-magnitude) values. This should be resolved on item creation... If that can be resolved, things shrink even more. Same can be said of the onchange callback: it could be initialized with empty regular or fat function and called unconditionally, all the times.

    Also: why is dir (ection in values and list) once value checked/defaulted and once not? (so I defaulted 2nd one too) ...and why is 1st one not positive 1 defaulted?

    Code_4: Minified - 318 byes vs. 460 as fixed Code_1 AND unminified code in post #2... 30% minified savings wow! <----------

    (using google closure compiler service all with var l, move = function(.... and manually 'prettied'):

    move: function(c,a,b){
      if(a=l.selectEdit){
        b=a.value-(c||1)*(a.step||1);
        a.value=b=void 0!==a.min&&b<a.min?a.min:void 0!==a.max&&b>a.max?a.max:b;
        if(a.onchange)a.onchange(b);
        l.draw(b=options.selected,b) }
      else b=((c||1)+(a=options.selected)+(b=menuIt­ems.length))%b
         , l.draw(Math.min(a,options.selected=b),Ma­th.max(a,b))
    };
    

    Note the shrinking of the tertiary min/max statements...

    --- I never ran into this issue - var ref not known and trying to overwrite global - even with the most obscure code in the https://espruino.com/UI. Surprisingly related: In the past, I had a manuscript editor for my publications who remembered whether I had defined / introduced a term or not before using it, even though the editor was not a subject matter expert, nor did the editor use the computer, and the publication could be several hundredths of pages long. It was an amazing - and painful - experience... to yield and get the things in sequence | order | defined... and that was just in natural language / English. Now, I see the same phenomenon in a formal language, JS, where the reader is not a human but a machine. Obvious conclusion: both need a proper setup of context to operate correctly!

    Lessons learned for Espruino: Smart intro, placement and use of intermediate 'work' variables vs. Fowler'sh demanded paradigm of referencing and some familiarity w/ (ESPRUINO's) interpreter's workings buys you A LOT OF FREE ESPRUINO VARS (bytes)!

    PS1: M. Fowler context: C/C++, Java, JS w/ compiler... - to be context-correct! - Clean Code and Refactoring are excellent readings.

    PS2: interested in feedback if above code works... (have no immediate 'machine' setup to validate).

About

Avatar for allObjects @allObjects started