• Mon 2018.10.08

    Thank you @MaBe for the working example:

    http://forum.espruino.com/conversations/­317394/#comment14111852


    Q1: While this.start() does work when testing a module as a monolithic file:

    constructor(option) {
            var timeStart = 0;
            var timeStop = 0;
            if( typeof option == "string" ) {
              if( option == "auto" )
                this.start();
    

    an error occurs when testing as a deployed module:

    >Uncaught Error: Function "start" not found!
     at line 5 col 18
                this.start();
    

    What is the correct syntax to reference an internal class method from inside that class?

    Indicates Class Definition 1v96 Official using 1v99

    http://www.espruino.com/Features



    Q2: An error occurs when using a multi-parameter constructor when a default constructor is present

    constructor() {
    
        // If option == "auto" auto start
          constructor(option) {
    

    ERROR: [notify_error] Input_0:14: ERROR - Class contains duplicate method name "constructor" in moduleTestConstructor

    Is the preferred method to use just the multi-parameter version and have implied optional arguments and code the single constructor for those options?

    e.g.
    if( typeof option == "undefined" ) {
    if( option != "undefined" ) {


    3 Attachments

  • Try this:

    when using one file than

    • rename class
    • use correct constructor call

    and there you go :-)

    exports = {};
    
    class StopWatch {
    //      constructor() {
    //        var timeStart = 0;
    //        var timeStop = 0;
    //      }
        // If option == "auto" auto start
          constructor(option) {
            var timeStart = 0;
            var timeStop = 0;
            if( typeof option == "string" ) {
              if( option == "auto" )
                this.start();
    //            start();
            }
          }
        // Method start()
          start() { 
            this.timeStart = Date.now();
            this.timeStop = 0;
          }
        // Method stop()
          stop() { 
            this.timeStop = Date.now();
          }
          // Method duration()
          duration(){
            return this.timeStop-this.timeStart;
          }
    }
    exports = StopWatch;
    
    
    //stopWatch = new (require('stopWatch'))();
    //stopWatch = new (require('moduleStopwatch'))();
    //stopWatch = new (require('moduleTestConstructor')("auto"­))();
    
    stopWatch = new (StopWatch)("auto");
    
    
    function onInit(){
          stopWatch.start();
          setTimeout( function(){
            stopWatch.stop();
            console.log('duration in sec: ',
                    ( stopWatch.duration() / 1000 ).toFixed(2) );
          }, 1000);
    }
    setTimeout(onInit,1000);
    
    
    
    
  • I see kind of a mixup of class vs instance method and the linking to module.

    First module has no direct connection to class. Only what is returned in a module can be interpreted as a plain function or constructor function, and latter can be understood as class method.

    In a Class based object-oriented language - which JavaScript is NOT (it's prototype based but can be 'emulated') - the class is (best) an actual (first class) object - and 'understands' only class methods, of which at least one is usually at constructer, either called a method - in ClassObject.new() - or with a language key word new ClassObject(). Latter is JavaScripts flavor and invokes the constructor with ES6 class definition or the previous style when it was just a method having things inside like this.stateProperty = aValue;. Any constructor returns an instance. With the term instance we come to the
    instance methods, which are only understood by instances.

    In a constructor, this refers to an instance and is the context of your setup.

    If you need to refer to a class method in an class or instance method, you just name the class. Since in JavaScript the constructor (function) is practically the only class method (except you attach function attributes to the constructor function), you repeat the same class or another class - latter pattern is common where as former is rarely found (except in chained / linked / tree-d type of constructs). An example of the rarer kind would be the create and return of a directory that needs a creation of a multi-level path with the option of auto-create when missing... The example has the oddity of wasting the just constructed instance when already existing. In that case it would explicitly say return foundDirectory;.

    Anyway, I hope this clears up some of the muddied terminology... which is difficult to respond to. It's like reading a riddle and then with the help of some 6th sense to assume what the comment or question is all about.

  • Mon 2018.10.08

    Attachment from #1 codeTestConstructorExports.js demonstrates working monolithic file:

    Thank you @MaBe for your effort, but only works as a monolithic file. When deployed, still has the same effect, which allObjects pointed out.
    stopWatch = new (StopWatch)("auto"); is missing the 'requires' keyword for module deployment.


    'I see kind of a mixup of class vs instance method and the linking to module.'

    Yes, agreed. I didn't think the casing of the class definition made a difference, and we are correct. It doesn't. See attached files.

    'instance methods, which are only understood by instances.'

    As the instance is aware of it's properties, such as this.timeStart, shouldn't the class methods also be recognized then? this.start();

    'invokes the constructor with ES6 class definition'

    May I ask how you determined this was a ES6 flavor? When I checked the 'features' link #1 above, I see 1v96 I made every effort to avoid any feature that may have played a role with the current version of the parser/compiler.

    '. . . which is difficult to respond to'

    @allObjects, totally agree and is also a challenge to make sure the correct wording is used to describe along with the simplest of snippets to get that point across.


    2 Attachments

  • @Robin, I (still) try to understand what the context is when you use the term 'deployed'. In Espruino that term is not used. But from reading through your post, I looks to me that with 'deployed' you want to say that it is a module and is pulled with require(....

    Is that how you would define your term 'deployed (as a module)' ?

    Within Espruino the term 'inline testing of module code is a known term and way to develop a module together with the application code in the same file (in the Espruino IDE. Once done with the development, the module part is put into modules folder (or where ever you can pull it with require(.... You use the term "monolithic file". I can understand that you look at some things from a different angle and names you are use to... but that makes it difficult to grok the context your question comes from or comment is about (btw, files are always monolithic from the point of a file (may be not from a point of operating / disk operating system that stores the file in some kind of blocks, contiguous or scattered in the space of the storage).

    So far the inline testing - as suggested in https://www.espruino.com/Writing+Modules­ for https://www.espruino.com/Modules- is something very brittle, because it uses the same name space... and that usually creates undesired side effects, constraints application in name choices, and worst, can lead to interference (breaking side effects).

    I started to use (also) a different approach to get the core issue - namespace problem - out of the way by loading the module cache directly, as described in the conversation about Module development - and shows explicitly in post #3.

    Inpost #3 you can see the module cache primed with the module source code and the module then retrieved by require(.... No omissions or additions and especially no name conflicts, undesired dependency nor forced dependency between application and module, commenting and uncommenting of different things at different times - all bad things... For example: now you can name the things in the module code the way you want and chose the name you want in the application code without worrying that there is a disconnect or conflict. Furthermore, it is absolutely clear what goes where: you just take the source and put it into a file into modules folder with the desired module name.

    In post #8, this technique uses a newer feature of Espruino which allows to store modules as functions in the modules cache. Initially, the cache loading could only happen by providing source code, which is a test string. At a later time, @Gordon introduced the storing as a function, which makes it equally simple to have the modules demarcation within the application when doing 'inline development and testing'.

    What ever you do in the module source - text string - post #3, lines 5 thru 33 - OR function body - post #8, lines 5 thru 42 in final code - is not visible to the application code what so ever. require(... exposes only what you want to let be seen by the application with the name to be used outside of the module... can be same or different of the name in the module. With class names, you can give a three different names, one each class name in the module, one for the module, and class name in the application... Usually, one strives for sanity reasons to give the module and the class the same name (especially when there is a 1-to-1 relationship between module and class: each class makes a module.

    Furthermore, it seems to me that you see a class is different whether the class is defined inline or in module. There is no difference, because module is not tackling the structuring of software in classes, tackles the issue of separating source code into files, even though latter can be used to achieve former in order to enable reuse of classes without having to copy-paste source code... ;-)

    I suggest to take a deep look into Module development. I think it could help answering some of your questions and to develop your concepts even further.

    Btw,

    1. Javascript allows only one constructor... you can the no to many parameter option... - but only one constructor. If you want a mix, you allow to pass a single object, and in the constructor you look what properties are in the passed object and use these, for the others you use defaults accessible from or hardcoded within the constructor.

    2. The lines 39 and 43 in the code in post #2 collide logically...

      • Line 39 creates a stop watch instance and starts it right away (with the "auto" parm). That happens when the code is uploaded (and save will then save its state at the time of saving... some time into the ticking...)
      • Line 43 uses the same instance and starts it again... Nothing may break that, but I'm not sure that is the intention... it would be for sure not mine. What could be done is use in line 39 not the "auto" nor any other parameter that gets the (code) bomb ticking... eeeehh, I meant stopwatch...
  • Mon 2018.10.08

    'Is that how you would define your term 'deployed (as a module)' ?'

    Yes. Wanted to definitely separate the test file, module included, from the remotely fetched from GitHub module, concept. As GitHub is a remote server, the module is deployed there.

    ' it seems to me that you see a class is different'

    Nope.

    As I started out in #1 above, the class functions fine when in it's test container, but fails when used as a separate code and module. Deployed if you will. ;-)

    It is also why I supplied individual files with leading 'code' and 'module' so that whomever used them would just know where to deploy them.

  • EDIT: just noticed that while I was editing/adding to the post, you started to respond and we were working on the same conversation at the very same time... and that showed for me in weird behavior of the forum: it would extremely slow down the typing... it took seconds until a character would show... so I deleted all in this post #7 because it was a repetition of 6# plus a few edits and additional lines.

  • I pointed out the issue with inline testing when in same namespace... and that's what is failing for you. The context is different when the module is delivered from when you run the module source code inline in the application... because there it NOT a module.

  • Mon 2018.10.08

    'any other parameter that gets the (code) bomb ticking...'

    I thought some would read too much into what the code's intention was.

    The dilemma. Use all day to take a ginormous file and whack at it to make it as concise as possible to make a feeble attempt to get to a simplified snippet to use as the source, or use a concise solution and make a few code line changes to get the point across. @MaBe had a nice easy to use proven solution that I chose instead.

  • and we were working on the same conversation at the very same time

    Yeah. Same here. Thanks for correctly indicating the edit in #7 as it did take me back when the content I was attempting to respond to, []



    . . . . vanished

  • Mon 2018.10.08 07:55pm CST

    Yikes, just heard on the national news that the latest Windows10 automatic update wipes out all the files in the 'Documents' folder!! . . . and they have no clue why!! 'Looking into it' they say. Good going Micro

  • when you run the module source code inline in the application... because there it NOT a module.

    Yes. I understand that. I'll repeat again. There is nothing wrong with the test file with the class (as a module) inline. The issue is when the module is used independently, called from a code file when using 'require()'

    From #1 above: "an error occurs when testing as a deployed module:"

    I also supplied that solution in 'codeTestConstructorExports.js' as proof.

  • (forum is messing with us again... so I try again:)

    ...but with an a logical error, as I point out in #8. Because the original code - writing modules using new feature class - interestingly *** also from @MaBe *** - does not start the stopwatch on creation... just as I suggested.

    There is nothing wrong to add the auto, because you do not want to have the extra code of starting the stopwatch. That's why you / @MaBe allow the "auto" to be passed. Makes absolute sense to me. To make the stopwatch robust though, it should object to start a started stopwatch with Illegal State Exception, which means that at the state the object is right now, a start is not possible... An additional property can indicate that the stopwatch is started or stopped. Combination of particular values of startTime and endTime could do that too: both are 0 or both are not 0 can do the test. Therefore:

         ...
        // Method start()
          start() { 
            if (    (this.timeStart === 0 && this.timeStop === 0)
                 || (this.timeStart !== 0 && this.timeStop !== 0)
               ) {
               this.timeStart = Date.now();
               this.timeStop = 0;
             } else {
                throw "Illegal State Exception - stop watch already already started / is running."
             }
          }
          ...
    

    See try...catch and throw in MDN...:

    1. https://developer.mozilla.org/en-US/docs­/Web/JavaScript/Reference/Statements/try­...catch
    2. https://developer.mozilla.org/en-US/docs­/Web/JavaScript/Reference/Statements/thr­ow

    Regarding your proof: works as designed / as by your setup. There is nothing that needs to be proofed. It is how the system works and nothing is found at fault with it.

  • Tue 2018.10.08

    'and that's what is failing for you. '

    No. No it is not what is failing me. Inline testing shows the code that will be in the module is syntax checking and run-time error free.

    'It is how the system works and nothing is found at fault with it.'

    I will agree with the inline file, but the issue is with the two file version as I have explained and why I started this thread.

    The class properties and the class methods are recognized in the inline version. The class properties are recognized but the class methods are not in the two file version.

    I am seeking a solution to the last (previous) sentence only, not assistance in logic as outlined below. Please re-read #1

    I explained in #9 the reason to make a simple modification to an existing working example.

  • Just coming to this afresh looking at just the first post (sorry if everyone else has already mentioned this), if you change:

    stopWatch = new require('moduleTestConstructor')("auto")­)();
    

    to

    stopWatch = new (require('moduleTestConstructor'))("auto­");
    

    It works great.

    Also, if you take your all-in-one file that works and change stopWatch = new stopWatch("auto"); to the equivalent of what you've done in the other example: stopWatch = new (stopWatch("auto"))(); then it breaks in exactly the same way.

    What you're doing is running the stopWatch constructor as a bare function (with stopWatch("auto")) and then calling new on that. If it ever got to executing new it'd fail, but as it is it just runs stopWatch("auto") and then throws an error because this isn't a stopWatch object.

  • IMNSHO, using object oriented (oo) terms is not a matter of code snippets... it's a matter of communicating apples and oranges... less talking about comparing them.

    A good introductory start is http://www.academia.edu/download/3216960­1/Object-Oriented_Software_Construction.­pdf - explains the concepts and the various language bindings.

    1400+ page is a very looooong start. So let's get a shorter one - 2 clips:

    1. https://medium.com/the-renaissance-devel­oper/python-101-object-oriented-programm­ing-part-1-7d5d06833f26 - intro into Class, Object (Instance), ...
    2. https://medium.com/the-renaissance-devel­oper/python-101-object-oriented-programm­ing-part-2-8e0db3ddd531 - intro into Encapsulation, Inheritance

    To bad that it is Python, but replacing self with this, _init_ with constructor and leaving out def, it is pretty close to JavaScript... after all, Python uses a lot (of the same as or) from JavaScript.

    And there is MDN with https://developer.mozilla.org/en-US/docs­/Web/JavaScript/Guide/Working_with_Objec­ts - at times confusing, because it shows all the options of oo pre-ES6 and ES6+ (and the ES6 modules aren't there yet... but the node.js).

    Now we do some changes to any code and discuss it...

    The shortest way though is sneak peek into the - or one of the best practice - solutions, like peeking at the solution for the Sudoku challenges in an airline magazine.

    Here the solutions, clear and straight, with all bells and whistles, and naming ad casing as best practices suggest (single saves and run... bulls-eye):

    // StopWatch 'class' as module "StopWatch.js" in modules folder
    class StopWatch {
          // constructor - class' (class) Method 'new' (instance)
          constructor(option) {
            var timeStart = 0;
            var timeStop = 0;
            if (option == "autostart")
               this.start();
          }
          // (instance) Method start()
          start() {
            if (    (this.timeStart === 0 && this.timeStop === 0)
                 || (this.timeStart !== 0 && this.timeStop !== 0)
               ) {
              this.timeStart = Date.now();
              this.timeStop = 0;
            } else {
              throw "IllegalStateException - stopwatch ticking.";
            }
          }
          // (instance) Method stop()
          stop() {
            if (this.timeStart !== 0 && this.timeStop === 0) {
              this.timeStop = Date.now();
            } else {
              throw "IllegalStateException - stopwatch not ticking.";
            }
          }
          // (instance) Method duration()
          duration(){
            if (this.timeStart !== 0 && this.timeStop !== 0) {
              return this.timeStop-this.timeStart;
            } else {
              throw "IllegalStateException - stop did not tick.";
            }
          }
    }
    exports  = StopWatch;
    
    // Application (project) using StopWatch class going as
    // stopWatchApp.js file into projects folder.
    var StopWatch = require('StopWatch'); // get class
    var stopWatch1 = new StopWatch(); // create instance
    function onInit(){
          stopWatch1.start();
          setTimeout( function(){
            stopWatch1.stop();
            console.log('stopWatch1: duration in sec: ',
                    ( stopWatch1.duration() / 1000 ).toFixed(2) );
          }, 1000);
          var stopWatch2 = new StopWatch("autostart");
          setTimeout( function(){
            stopWatch2.stop();
            console.log('stopWatch2: duration in sec: ',
                    ( stopWatch2.duration() / 1000 ).toFixed(2) );
          }, 2000);
          var stopWatch3 = new StopWatch("autostart");
          try {
            stopWatch3.start();
          } catch(x) {
            console.log('stopWatch3: start() exception: ',x);
          }
          try {
            console.log(stopWatch3.duration());
          } catch(x) {
            console.log('stopWatch3: duration() exception: ',x);
          }
    }
    setTimeout(onInit,1000);
    

    Output attached as screenshot.

    Notice the time sequence... StopWatch instance 3 gets bothered before instance 1 and 2 stop and show the duration.

    Instead of getting the class (line 3) and re-use it with all the new, one could require the class for every new (with not much performance penalty): ... = new (require("StopWatch"))() and ... = new (require("StopWatch"))("autostart"). Instead of throwing simple text exception, Error or RYO Exception class or object in literal notation or JSON could be used.


    1 Attachment

    • stopWatchAppConsoleOutput.png
  • Wow great stuff @allObjects- thanks for sharing.

  • Tue 2018.10.09

    Ref #15 Short and precise along with the paragraph explanation. That is what I was after. Thank you @Gordon.

    Placing the '()' and the argument correctly gave me fits, even with the IIFE construct present in the original source.

    I'm researching 'bare functions' as I've not heard that term before.

  • 'bare function' is when it is not a method function of an object - that means it does not need or use a this context - and gets variable parts - if needed - as parameters. But since there is always a context, it is somehow cobbled up by the interpreter. For this reason this is often NOT what we expect to be, undefined, or does not understand what we ask it for - for state or for behavior. See MDN https://developer.mozilla.org/en-US/docs­/Web/JavaScript/Reference/Global_Objects­/Function/apply

    That this functionXyz.apply(thisArg, argArray) works is a core nature of JavaScript. To give you examples:

    Think of two objects that have both a width and a length in rectangular form that defines their foot print, in addition to other things, that can be totally different - for example shipping container (8 feet wide, 8.5 feet tall and of different length (or flooring tiles) and you need to know the total surface - footprint - these objets cover (can cover) when not stacked.

    Btw, while reading this, paste the code pieces into the console... and you will see the results right away.

    var obj1 = {"w":8, "h":8.5, "length":20,  "name":"object1",  "propertyFoo":"Foo"};
    var obj2 = {"w":8, "h":8.5, "length":40, "name":"object2",  "propertyBaz":"Baz"};
    var objects = [ obj1, obj2 ];
    

    Now you can define a function that, when applied to any of these objects delivers you the footprint in square feet:

    function footPrint() { // for example, container foot print
       var area = this.w * this.length;
      console.log(this.name+' covers '+area+'sf.');
      return area;
    }
    

    Question: how in the world does this footPrint know what this means - refers to. If you invoke the bare function, you will get an error - probably, this.w, .length and .name not defined, because this is not what you expect it to be, and it may throw different errors when called in different contexts... some they may have a property w, length and name, but with a totally different meaning.

    footPrint();
    

    When applying this function to an object that it can handle or can be handled by it, we get what we want:

    footPrint.apply(obj1);
    footPrint.apply(obj2);
    

    And when we want the total we do this:

    objects.reduce(function(sum, object){ return sum + footPrint.apply(object); }, 0);
    

    and get 480.

    Now, we make it a bit more elaborated: We want to know what the volume is these container objects can store: how many cubic feet of what ever they can store. It is easy to understand that the longer container have a stronger structure - beams, walls, etc. and may therefore have less storage volume. Therefore we create a volume() function for both lengths, conveniently named:

    function volume20() {
       var volume = (this.w - 2*0.5) * (this.length - 2*0.6) * (this.h - 0.8 - 0.4);
      console.log(this.name+' stores '+volume+'cf.');
      return volume;
    }
    function volume40() {
       var volume = (this.w - 2*0.7) * (this.length - 2*8) * (this.h - 0.8 - 0.4);
      console.log(this.name+' stores '+volume+'cf.');
      return volume;
    }
    

    Fort the total volume we use:

    objects.reduce(function(sum, object){
       var volumeFunction = global['volume'+object.length];
       return sum + volumeFunction.apply(object); }, 0);
    

    and get 2116.9999999999995

    Now, you say, why all this mess, because we could define a Container class and have a constructor that takes values, and then instance methods for .footPrint() and .volume() and what ever we need for convenience... and we can make it even inheriting: Container is the super class and entry class for constructor invocation and we pass a length and get instances of Container20 or Container40 sub class instances es back.

    Exactly, this is what ES6 gives us. Deep under the hood though, exactly what we so cumbersome experienced, happens, even ES6 does under the hood the XyzConstructorFunction.prototype.abc = pattern... at least logical. But that is not the full truth, because we can use these mechanism for all kinds of dynamic things...

    In this context belongs also the .bind().... see MDN https://developer.mozilla.org/en-US/docs­/Web/JavaScript/Reference/Global_Objects­/Function/bind.

    This allows us to invoke a bare function, but because it was bound before to an object, it is as if we invoke it as a method on the bound object. This is helpful for all cases where we can only pass a (callback) function, such as in setTimeout, setInterval, setWatch, and in many modules where things happen asynchronously or time deferred and we do not want to block the processor.

    var boundFootPrintObj1 = footPrint.bind(obj1);
    setTimeout(boundFootPrintObj1,1000);
    

    Executed we get delayed 160 as expected.

    What .bind() does is returning a new anonymous function that knows to apply the function mentioned to the bound object.

    Sane is for the an ES6 created object, that would have the method .footPrint().

    What we then say (in pre ES6) is:

    function Obj(name) { this.w=8; this.length=20; this.name=name; }
    Obj.prototype.footPrint=function(){var f = this.w*this.length; 
       console.log(this.name+' covers '+ f+'sf');}
    var obj = new Obj("obj");
    var boundFootPrintObj = obj.footPrint.bind(obj);
    setTimeout(boundFootPrintObj,1000);
    

    The first part of the right side in first line obj.footPrint provides as the reference to the method definition, which is actually only a function (attached with the .prototype to the constructor function in ES5, and ES6 tersed and sugar coated or us). The bind then returns - again - a new anonymous function that knows to apply the 'method' function to the bound object, thru which we even found the actual method in the first place.

    The ES6+ version I leave up to you to do... ;-) piece of cake... and I can have the cake and eat it.

  • Regarding the ()...:

    Think about of these as the execution or invocation method of the function object. In JS, all things are object... sounds like my screen name... does it? ...and sorry when it feels 'me being obsessed with objects'... I'am, kind-a, but it gets and keeps my daily job robustly going.

    Therefore, functions are objects as well. To 'get them going', you just throw () at them without parameters, or with a parameter list (parm1,parm2,...), and there you have it... thats why you find https://developer.mozilla.org/en-US/docs­/Web/JavaScript/Reference/Global_Objects­/Function as prototype (class) definition, with all its methods, such as .bind()... JavaScript syntax makes then life easy for you and provides you with the short cut:

    function myFun() { console.log("haha"); }
    

    which is nothing else then:

    var myFun = function() { console.log("haha"); }
    

    or even more cumbersome:

    var myFun = new Function('console.log("haha");');
    

    Entering

    myFun
    

    just returns the function as object

    myFun()
    

    fires it to say: haha

    Interesting - but expected from how JS works - is when requiring module that returns a constructor (class), that the require has to be 'precedenced' / precedence enforced with parentheses, and only after that requires completes, the new with or without parameters in parentheses can be thrown/applied at it:

    var stopWatchAutoStarted = new (require("StopWatch"))("autostart");
    

    Otherwise, the new goes against the require, and require is not a constructor with the module name as a parameter / will fail as a constructor... This fact shows the weird or mixed design of the new key word as part of the language syntax. In a clean oo language this would read: require("ModuleReturninClass").new(param­1, param1,...), with .new(...) being a class method. require(...) would then also not be a free floating function... it has at least to be a class method of the Module class... Because in a clean oo language practically only labeled instances are globally known, and classes are instances of the class Class and are labeled by their name, they are globally visible.

    There is more to - this - OO and Lambda (Calculus) constructs... Lambda Calculus, - something 'discovered'/formulated in the 30'. ...and about that may be at another occasion... -

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

Correct syntax to reference a class function inside a class as a module

Posted by Avatar for Robin @Robin

Actions