Variable as a read-only property in object

Variable as a read-only property in object
0

#1

I have a function constructor and I’m trying to declare some variables as read-only properties, here’s how it looks:

function Stopwatch() {
  let duration = 0;

  let props = {
    startTime: "",
    endTime: "",
    running: ""
  };

  Object.defineProperty(this, "props", {
    get: function() {
      return props;
    }
  });

  Object.defineProperty(this, "duration", {
    get: function() {
      return duration;
    }
  });
}

I am also trying to use prototype to inherit some methods of this object, this is one of the methods:

Stopwatch.prototype.start = function() {
  let { running, startTime } = this.props;
  if (running) throw new Error("Stopwatch has already started.");

  running = true;

  startTime = new Date();
};

If I define a new object const sw = new Stopwatch() and then call sw.start() twice I should get an error in the console, but nothing is happening. It’s like if I’m not reassigning values to the keys of the props object, so it’s like if running is always false…

If instead of the props object I had variables, like this

let startTime, endTime, running;

How can I make these variables public to my methods through prototype inheritance so I could reassign values to them? I know I could use the same process I used with the duration variable, but I don’t want to repeat that for every variable, there must be a better way.


#2
function Stopwatch() {
  this.startTime;
  this.endTime;
  this.running;
  let duration = 0;

  Object.defineProperty(this, "duration", {
    get: function() {
      return duration;
    }
  });
}

Stopwatch.prototype.start = function() {
  let { running, startTime } = this;
  if (running) throw new Error("Stopwatch has already started.");

  running = true;

  startTime = new Date();
};

The above solution is not working either after I define const sw = new Stopwatch() and call twice the sw.start() method. I should get the error in the console after I call the start method the second time, but it’s not doing anything, as if running is still false.


#3

It should be:

this.running = true;

#4

In my second example? But why? I used destructoring, why wouldn’t it work in this scenario? @RandellDawson


#5

Because in the original function declaration of Stopwatch you never assign it a value, so it is undefined by default. When you call the start function the first time, this.running is still undefined which is a falsy value. Your if statement (below) is checking if running has a truthy value which at this point, it is not.

if (running) throw new Error("Stopwatch has already started.");

This means, the throw does not execute and then next line (below) creates a global variable named running with a value of true.

running = true;

Unless you specifically assign the value true to this.running, the throw will not execute.

If you run the code below, you will see that in the first call of start, .running is undefined after destructuring this.

Stopwatch.prototype.start = function() {
  console.log('start function called')
  let { running, startTime } = this;
  console.log('running is')
  console.log(runnning) // you will get a ReferenceError: runnning is not defined
  if (running) throw new Error("Stopwatch has already started.");

  this.running = true;

  startTime = new Date();
};
const sw = new Stopwatch();
sw.start();

#6

Your solution works, but I am still confused as why I have to use thethis key word with the running variable when I try to reassign a value to it.

I mean, I want running to have a falsy value at first, so when I call start method the first time the function skips the if statement block of code and execute the other block of code. Here’s my logic:

Stopwatch.prototype.start = function() {
// Use destructuring to get running and startTime
  let { running, startTime } = this;
// The first time I call this method, skip this block of code because running originally has a falsy value
  if (running) throw new Error("Stopwatch has already started.");
// Reassign running to true, so the next time the method is called it throws the error above
  running = true;

  startTime = new Date();
};

If I assign running to 0, would it change anything? 0 is still considered a falsy value if I’m not mistaken.


#7

It would not change a thing, because the part you seem to be misunderstanding is no matter what value you assign to running after the if statement, it has does not change the value of this.running, which is what you really need to change.


#8

So… If I do this running = true, I’m creating a global variable, but if I do this instead this.running = true, am I reassigning a value to the running variable that I declared in the parent function StopWatch? The latter is exactly what I tried to do from the beginning.


#9

Basically, but I would rephrase it as:

You are reassigning the value of the property named running of the stopWatch function.

Why? Because this.running is not a variable. You are just defining a property on the stopWatch function.


#10

Yes, my mistake, it is a property, that’s exactly the reason why I used object destructuring to get the properties that I want to work with in each method. In the case of start method, I just need running and startTime. This way, I don’t need to use the this key word every time I use those properties in the given method.

But somehow, I still need to use the this key word when I try to assign a value to the running property, even though I already used destructuring to work with this property?


#11

The destructuring just creates new variables with their own values which are not unconnected to this. this still refers to the stopWatch function, so if you want to change any of the properties on the stopWatch function, you will have to refer to them as this.whatEverPropertyName. There is no way around that.


#12

Umm I think I got things mixed up with React then, people usually use destructing to get the props of a component.

So basically, if I assign a value to a property in the parent object (Stopwatch) I can use destructuring in whatever method to get the value of that property and use it multiple times in said method without the need of using the this keyword. BUT, if I want to reassign a value to a property of the parent object (Stopwatch) in a given method, I need to use the this keyword to change the value of that property in the parent object?


#13

Yes. It is kind of what setState does in react. It updates all the state properties for you, so you are not writing this.someProp. Of course, setState does more than that, but you get my point hopefully.


#14

Yes, I do get your point. Thank you very much, now things are starting to make more sense.


#15

What about this structure?

function Stopwatch() {
  let startTime;
  let stopTime;
  let running = false;
  let duration = 0;

  Object.defineProperty(this, "duration", {
    get: function() {
      duration = stopTime - startTime;
      return duration;
    }
  });

  Object.defineProperty(this, "start", {
    get: function() {
      if (running) throw new Error("Stopwatch has already started.");
      running = true;
      startTime = new Date();
      return startTime;
    }
  }); 

  Object.defineProperty(this, "stop", {
    get: function() {
      if (!running) throw new Error("Stopwatch has already stopped.");
      running = false;
      stopTime = new Date();
      return stopTime;
    }
  });   
}

const sw = new Stopwatch();
console.log('start time = ' + sw.start); // displays start time;
setTimeout(function() {
  console.log('stop time = ' + sw.stop) // stops the stopwatch and after 2 seconds
  console.log('number of milliseconds passed is ' + sw.duration) // displays 2000 (time passed in ms)
},2000);