instantiate an object within another object?

Posted on
  • I'm not sure what the terminology is called.... I am trying to instantiate an object within another object.. I am getting these errors:

    Uncaught Error: Field or method does not already exist, and can't
    create it on undefined at line 2 col 10
    this.ph.updateResTemp(tempCompensation);­

          ^ in function "getPhValue" called from line 2 col 22   bot.getPhValue(90.5);
                      ^
    

    Code:

    I2C1.setup({scl:b6, sda:b7});
    
    Serial4.setup(9600, {tx:C10,rx:C11});
    
    function s7s (theAddress) {
      this.address = theAddress;
      this.displayTable = {
        "Clear" : 0x76,
        "dp"    : {
          "cmd" : 0x77,
          "location"   : [
              0b00000001, //Digit 0
              0b00000010, //Digit 1
              0b00000100, //Digit 2
              0b00001000  //Digit 3
          ]
        }
      };
    }
    
    s7s.prototype.writeValueToDisplay = function () {
      var toChar = "";
    
      var a = this.address;
      var clrCMD = this.displayTable.Clear; //Display clear command
      var dpCMD = this.displayTable.dp.cmd; //Decimal point command
      var dpLocation = this.phDisplay.displayTable.dp.location;­ //Decimal point location map
    
      I2C1.writeTo(a, clr); //Clear display
    };
    
    function Sensor (theType, theAddress) {
      this.type = theType;   //i.e. PH
      this.address = theAddress;  //i2c Address
      this.sensorResult = 0; //Store sensor result
      this.cmdTable = {
        "Calibrate" : {  //List of Calibration commands and timeout value.
          "clear" : { "cmd" : "Cal,Clear",      "wait" : 300  },
          "mid"   : { "cmd" : "Cal,mid,7.00",   "wait" : 1300 },
          "low"   : { "cmd" : "Cal,low,4.00",   "wait" : 1300 },
          "high"  : { "cmd" : "Cal,high,10.00", "wait" : 1300 },
          "query" : { "cmd" : "Cal,?",          "wait" : 300  }
        },
        "Information" : {  //Device Information
        },
        "LED" : {  //Enable / Disable or Query the LEDs
          "L0" : { "cmd" : "L,0", "wait" : 300 },
          "L1" : { "cmd" : "L,1", "wait" : 300 },
          "L?" : { "cmd" : "L,?", "wait" : 300 }
        },
        "Reading" : {  //Takes a single reading
          "R" : { "cmd" : "R", "wait" : 1000 } //Takes a single temperature compensated reading
        },
        "Serial"      : {  //Switch back to UART mode
        },
        "Sleep"       : {  //Enter low power sleep mode
        },
        "Status"      : {  //Retrieve status information
        },
        "Temperature" : {  //Set or Query the temperature compensation
          "T"  : { "cmd" : "T,19.5", "wait" : 300 },  //Where the temperature is any value; floating point, or int, in ASCII form
          "T?" : { "cmd" : "T,?",   "wait" : 300 }  //Query the set temerature
        },
        "Factory"     : {  //Factory reset
        },
      };
    }
    
    Sensor.prototype.getSensorType = function () {
      return this.type; //Get Sensor type
    };
    
    Sensor.prototype.getSensorAddress = function () {
      return this.address; //Get Sensor address
    };
    
    Sensor.prototype.getSensorReading = function() {
      var a = this.address;
      var d = I2C1.readFrom(a, 7);
      return d;
    };
    
    Sensor.prototype.getSensorResult = function () {
      var a = this.address;
      var c = this.cmdTable.Reading.R.cmd;
      var w = this.cmdTable.Reading.R.wait;
      var that = this;
    
      I2C1.writeTo(a, c);
    
      setTimeout(function (e) { callback(that.getSensorReading()); }, w);
    };
    
    Sensor.prototype.storeSensorResult = function () {
    };
    
    Sensor.prototype.updateResTemp = function (temp) {
      var a = this.getSensorAddress();
      var c = this.cmdTable.Temperature.T.cmd;
      var w = this.cmdTable.Reading.R.wait;
      var that = this;
    
      Serial4.print(a + " " + c + "\r\n");
    
      I2C1.writeTo(a, c);
      setTimeout(function (e) { that.getSensorReading(); }, w);
    };
    
    function processData () {
      //Sensor Objects
      var ph = new Sensor("ph", 0x63);
      var ec = new Sensor("ec", 0x64);
    
      //Sensor Displays
      var phDisplay = new s7s(0x71);
      var ecDisplay = new s7s(0x72);
    }
    
    processData.prototype.getResTempValue = function () {
    
    };
    
    processData.prototype.getRoomTempValue = function () {
    
    };
    
    processData.prototype.getRoomHuminityVal­ue = function () {
    
    };
    
    processData.prototype.getPhValue = function (tempCompensation) {
      this.ph.updateResTemp(tempCompensation);­
    };
    
    processData.prototype.getEcValue = function (tempCompensation) {
    
    };
    
    Serial4.on('data', function (data) {
      Serial4.print(data);
    
      var edisonData = data;
      Serial4.print("Edison sent: " + edisonData);
    
    });
    
    var bot = new processData();
    
    setInterval(function (e) {
      bot.getPhValue(90.5);
    }, 3000);
    
  • Can you please post all the code?

  • I think it's because you define var ph and not this.ph?

    Worth checking what's actually in the variable 'bot'

  • @DrAzzy I edited my original post with all the code. It's really messy and eventually I will split my code up into separate files to later include them.

    @Gordon I checked what's in bot by doing:

    setInterval(function (e) {
      //bot.getPhValue(90.5);
      console.log(bot);
    }, 3000);
    

    I receive what looks like an empty object:

    { }

  • How did I miss that at first glance, yeah it's definitely what Gordon said (maybe something else too).
    This should work...

    
    function processData () {
      //Sensor Objects
      this.ph = new Sensor("ph", 0x63);
      this.ec = new Sensor("ec", 0x64);
    
    
    
  • @Gordon @DrAzzy
    I don't understand why this works and var doesn't. Is it because this places the ph variable within the scope of processData?

  • Yes - in JavaScript, the only real difference between executing a method call and a normal function is what this is set to.

    So, if you define the variable ph in a function, it just stays in that function - but it's nothing to do with the class at all. To actually access the class, you have to explicitly use this..

  • Just to complicate things, the value of this actually depends on how the function is called.

    A function's this keyword behaves a little differently in JavaScript compared to other languages. (…) In most cases, the value of this is determined by how a function is called. It can't be set by assignment during execution, and it may be different each time the function is called.

    https://developer.mozilla.org/en-US/docs­/Web/JavaScript/Reference/Operators/this­

    Which is why we have .bind(), .apply() and .call(). Learn to love those, while they feel a bit like a hack they're also very powerful :)

    Also worth reading to understand the flexibility of functions/objects:
    https://developer.mozilla.org/en-US/docs­/Web/JavaScript/Reference/Functions

  • There is currently a conversation going in that respect - Serial.on() issues - which is actually more a about this, context classes, objects (instances of classes), functions, methods, callbacks, etc.

    Javascript has a bit a quirky looking way of instantiating an object from a given class: issuing var inst1 = new MyFunction() makes MyFunction() a class definition - or constructor function - that defines - and constructs - (usally the state of) an object - hence the convention of starting Uppercase. Issuing it a second time var inst2 = new MyFunction() creates actually a second, different instance - but with the same properties - state (and behavior).

    Invoking a capability, function, or behavior of that class of objects is then more suitably called invoking a method. Defining methods (behavior) is done by assigning functions to the prototype of the (state) defining function.

    To make it more obvious, lets, call the "MyFunction" a "Person", and provide it with a name 'when born' / created / instantiated:

    var Person = function(name) {
      this.name = name;
    };
    

    Now we provide a behavior to the Person - class of instances:

    Person.prototype.introduceYourself = function() {
      console.log("Hi, my name is " + this.name);
    };
    

    Creating susan and mark and ask them to introduce themselves would then go like this - notice the 'politically not so correct all lower case beginning, but convention complying useful distinction of instance vs. class casing:

    var susan = new Person("Susan");
    var mark = new Person("Mark");
    susan.introduceYourself(); // ---> console shows: Hallo, my name is Susan
    mark.introduceYourself(); // ---> console shows: Hallo, my name is Mark
    

    Note that we had to make the definition only once as a class, but get multiple instances with same state - .name - and behavior - .introducesYourself().

    Unfortunately, the way (most) modules are built and used is the way of singleton. Singleton means: there can exists only / exactly (math.) one instance of that class (so not really a class, because what is a class with one member...). Bit what do you do when you have multiple sensors, and the sensor is made available / connectable via a module that is thinking 'in singleton' (only)?

    Sometimes, you get away by storing the module in a variable and invoke it several times with connect. This works though only when the module is built in a particular way and returning the instance from the constructor function. (Depending how the require("moduleName") is implemented (with or without caching), instead of storing the module reference in a variable, you just repeat the require(...) with no significant performance penalty.)

    You can instantiate objects within other object instantiations... nothing wrong with that. It just creates dependencies - which you may have anyway. A less intertwined approach would be to create the containing object with null values for the contained objects first, and then assign the object instance to in a separate step, where you compose the complete containing object (or you go very sophisticated and use a micro / pico container supporting IoC / DI ... pico has nothing to do with Espruino Pico except that it is a smaller version of a bigger thing...)

    In order to access properties - state and behavior - of a contained object, you can call the objects nested - containingObj.containedObj1.getTemperatu­re(); - which is not the best from an oo point of view. You rather add a facade or delegate method .getTemperature() - ContainingObj.prototype.getTemperature = function() { return this.containedObj1.getTemperature(); }; - to the containing object. With that you can hide the implementation of ContainingObj and you can change it at a later time without having to change where and how it is used (so far).

  • @allObjects Thanks for your explanations and example code. I have such a limited knowledge of Javascript and I am learning tuns about javascript from my project.

    I wrote the majority of my pseudo code out on a piece of notebook paper at a coffee shop a few months ago. It's amazing how simple I though my code was going to be until I actually sat down and started writing code.

    I have no idea how individuals such as @Gordon do this for a profession. I write code that sorta works, take a break and then I completely forget everything that I wrote or how it works. Eventually, I will have an 'ahha moment' and all this code will make sense.

  • @Joakim Interesting... I remember I asked a question about .bind() in one of my other posts. I will have look more into .bind() .apply() and call()

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

instantiate an object within another object?

Posted by Avatar for d0773d @d0773d

Actions