Search code examples
javascriptobjectmoduleprototypejs

Modulepattern and instances of objects - unexpected changing of propertyvalues


Dear community and Javascript wizards,

I encountered a behavior I can not make sense of.

My intention is to create a module, which simulates private variables and controls the creation of objects.

I hope the following example illustrates what I want and what my problem is.

const ex = (() => {

  // Creating the constructor for points
  function Point() {
    let x;
    let y;
    
    // I think using prototype is a good idea, because it can be expected that a lot of points are created. So I do not waste memory with a lot of function duplications. 
    Point.prototype.setX = xValue => {
    x = xValue;
    }
    
    Point.prototype.setY = yValue => {
    y = yValue;
    }
    
    Point.prototype.getY = () => {
    return y;
    }
  }
  
  // Returning an interface to create points 
  // but hide their properties.
  return {createPoint: (x,y) => {
    let point = new Point();
    point.setX(x);
    point.setY(y);
    return point;
  }}
})();



p1 = ex.createPoint(1,2);
console.log(p1.getY()); // Result is '2' as expected

p2 = ex.createPoint(3,4);
console.log(p2.getY()); // Result is '4' as expected

console.log(p1.getY()); // Result is '4'..Wait...'4' what? Why?!

I think the obvious source is my lake of understanding the scope... My assumptions were/are that if I create a function as a prototype-property: 1. The function is for all objects of the created kind visible. 2. The prototype function operates on the scope of the object, which is using it.

From my results, I doubt that number 2 is correct.

So I gave it another shot and assigned the properties and methods directly to the newly created object (I hope I did so...)

const ex = (() => {

  // Creating the constructor for points
  function Point() {
    this.x;
    this.y;
    
    // I think using prototype is a good idea, because it can be expected that a lot of points are created. So I do not waste memory with a lot of function duplications. 
    this.setX = xValue => {
    x = xValue;
    }
    
    this.setY = yValue => {
    y = yValue;
    }
    
    this.getY = () => {
    return y;
    }
  }
  
  // Returning an interface to create points 
  // but hide their properties.
  return {createPoint: (x,y) => {
    let point = new Point();
    point.setX(x);
    point.setY(y);
    return point;
  }}
})();



p1 = ex.createPoint(1,2);
console.log(p1.getY()); // Result is '2' as expected

p2 = ex.createPoint(3,4);
console.log(p2.getY()); // Result is '4' as expected

console.log(p1.getY()); // Result is '4'..Wait...'4' what? Why?!

But the result did not change and I can not make sense of the behavior. After rereading the chapters about closures and prototypes in my js-bible, I have no idea where I can search or find help than asking you.

I would be glad if you could point out my error to me and explain what is going wrong in my code.

Kind regards

Jim


Solution

  • There are some problems on your code :

    • The way you are using the arrow functions. There is no own 'this' object in arrow functions. So using them to define a prototype will get weird results.

    • Talking about this? Ah yes, you need to use 'this' to store a value somewhere in the class instance.

    • It appears that there is no issue to use ES2015 (ES6) in your case. Why not use ES2015's 'Class' to define a class? (It does not give you real private props either, but syntax is very much improved)

    • Creating a class instance in a function in another function or closure does not really hides the properties of the class. Since you are returning that object, the values can be accessible over it.

    Strictly securing the must-be private properties / methods of class objects is not something really necessary. It is advised to give them some prefix (_ is common) and whenever you hit this prefix on your code, use that var only privately.

    const ex = (() => {
    
      // Creating the constructor for points
      function Point() {
      };
    
      Point.prototype.setX = function(xValue) {
      console.trace('hmm');
        this.x = xValue;
      };
    
      Point.prototype.setY = function(yValue)  {
        this.y = yValue;
      };
    
      Point.prototype.getY = function() {
        return this.y;
      };
      
      return {
        createPoint: (x, y) => {
          let point = new Point();
          point.setX(x);
          point.setY(y);
          return point;
        }
      }
    })();
    
    
    
    const p1 = ex.createPoint(1, 2);
    console.log(p1.getY() + " (Result is '2' as expected)"); 
    
    const p2 = ex.createPoint(3, 4);
    console.log(p2.getY() + " (Result is '4' as expected)");
    console.log(p1.getY() + " (Result is '2' as expected)");

    And here is an example to have a class with some real private members :

    const Point = function(valueX, valueY) {
      let x = valueX;
      let y = valueY;
      this.getY = () => {
        return y;
      };
    };
    
    const p1 = new Point(1, 2);
    console.log(p1.getY() + " (Result is '2' as expected)"); 
    
    const p2 = new Point(3, 4);
    console.log(p2.getY() + " (Result is '4' as expected)");
    
    console.log(p1.getY() + " (Result is '2' as expected)");