-1

I am writing some Javascript for a Change Calculator exercise. One of the requirements is to use object constructors; however, I am having a lot of trouble accessing a property in my object, from within one of the object's methods.

Here is my object:

function Coin(intValue){
    this.intCents = null; //The property

    function setCents(intValue) { //Trying access the intCents property from within this method

        /// <summary>Sets the intCents property for this object. If the given value is invalid, then the intCents property is given NaN.</summary>
        /// <param name="intCents" type="Number">The new intCents value.</param>
        if (isValid(intValue)) {
            this.intCents = intValue; //---javascript bugs out here!---
        } else {
            console.error("Invalid value given to Coin Object.");
            this.intCents = NaN;
        }
    }

    setCents(intValue);

    function getCents() {
        /// <summary>Returns the value of intCents.</summary>
        /// <returns type="Number">The intCents value.</returns>
        return this.intCents;
    }
}

and here is how I use my object:

var objCoin;

var calculateChange = function () {
    if (objCoin != null) {
        objCoin.setCents(Number.parseInt("67"));
    } else {
        objCoin = new Coin(Number.parseInt("67"));
    }
}

I took out some unnecessary code, but overall the basic functionality goes as following: User enters a number in a textbox and then clicks a button on a webpage. The button calls calculateChange method through the onclick event.

Inside the calculateChange method where "67" is, would normally be some code to get the textbox value. To simplify the code and provide a specific example of what type of value would be passed in, I replaced it with the "67".

What is post to happen is, assuming the user enters an integer...

If my objCoin variable is not instantiated with a Coin instance, then create a new instance and pass in the user's input through the Coin's constructor. If my objCoin is instantiated, then use the setCents method to hold the user's input.

Here is an screenshot showing what is happening and where the javascript bugs out: enter image description here

From my understanding, the issue appears to be with the "this" key word, as it is apparently undefined in if-statement. Despite this, I don't see anything wrong with my code.

From what I understand in my classes, this.intCents = null; is the correct way to create a property for the Coin object, and this.intCents would be the correct way to refer to the property from within the object regardless of where exactly in the object it is.

I tried reading up on the following questions:

'this' is undefined in JavaScript class methods seems to talk more about the new keyword and accessing properties from already created objects.

How does the "this" keyword work? had a lot of interesting example; however, I still don't see the flaw in my code.

Where am I going wrong with accessing the intCents property?

What is this in this scenario? (I believe it's object variable objCoin)

Programs Used:

Visual Studio 2015 Enterprise

Google Chrome and its Developer Tools

Community
  • 1
  • 1
Tyler
  • 809
  • 9
  • 23
  • Use an arrow function, save this to a variable outside, make the function an instance method, etc. – Andrew Li Mar 28 '17 at 00:32
  • may I ask why I was downvoted? ... – Tyler Mar 28 '17 at 00:33
  • That can't possibly be your full `Coin()` function, otherwise `objCoin.setCents(...)` would throw an error since it wouldn't be defined. – Patrick Roberts Mar 28 '17 at 00:35
  • Regarding `setCents(intCents)`, that was actually at typo I made when I was trying some things to fix the code. The typo has been fixed. – Tyler Mar 28 '17 at 00:36
  • @Tyler that's not what I'm talking about. In your code, you don't attach `function setCents() {...}` to `this`, so after the script exits the constructor, it would be garbage collected along with `function getCents() {...}`. – Patrick Roberts Mar 28 '17 at 00:39
  • I think everyone is not noticing that you didn't declare your var so `var intCents = null;` – Luis Estevez Mar 28 '17 at 00:39
  • @Patrick Roberts Oh I need to store it in a variable or something? – Tyler Mar 28 '17 at 00:39
  • Probably a duplicate of [*How does the “this” keyword work?*](http://stackoverflow.com/questions/3127429/how-does-the-this-keyword-work) – RobG Mar 28 '17 at 00:40
  • @Luis Estevez This is the proper syntax according my instructor, her demos and her powerpoint slides. – Tyler Mar 28 '17 at 00:40
  • @RobG i linked that question in my answer, saying that I couldn't figure out my problem with those answers. Even as a duplicate, it is still a valid question as I still need help. – Tyler Mar 28 '17 at 00:41
  • @Tyler did you at least give it a try? the debugger does says it's undefined. declaring `var intCents = null;` and I would read the link about this keyword that @RobG commented – Luis Estevez Mar 28 '17 at 00:48
  • @LuisEstevez like I mentioned right above your last comment, I did read and link the question robG commented... – Tyler Mar 28 '17 at 00:56

2 Answers2

2

You need to define the functions as properties of your created object like this:

function Coin(intValue){
    this.intCents = null; //The property

    this.setCents = function(intValue) { //Trying access the intCents property from within this method

        /// <summary>Sets the intCents property for this object. If the given value is invalid, then the intCents property is given NaN.</summary>
        /// <param name="intCents" type="Number">The new intCents value.</param>
        if (isValid(intValue)) {
            this.intCents = intValue; //---javascript bugs out here!---
        } else {
            console.error("Invalid value given to Coin Object.");
            this.intCents = NaN;
        }
    }

    this.setCents(intCents);

    this.getCents = function() {
        /// <summary>Returns the value of intCents.</summary>
        /// <returns type="Number">The intCents value.</returns>
        return this.intCents;
    }
}

Even better: Apply it to the prototype of your constructor function:

function Coin(intValue){
    this.intCents = null; //The property
    this.setCents(intCents);

}

Coin.prototype.setCents = function(intValue) { //Trying access the intCents property from within this method

        /// <summary>Sets the intCents property for this object. If the given value is invalid, then the intCents property is given NaN.</summary>
        /// <param name="intCents" type="Number">The new intCents value.</param>
        if (isValid(intValue)) {
            this.intCents = intValue; //---javascript bugs out here!---
        } else {
            console.error("Invalid value given to Coin Object.");
            this.intCents = NaN;
        }
    }

    Coin.prototype.getCents = function() {
        /// <summary>Returns the value of intCents.</summary>
        /// <returns type="Number">The intCents value.</returns>
        return this.intCents;
    }

What you did instead was: You created a named function (which is similar to, though not exactly the same as, creating a variable). This definition took place within the constructor function, so its "visibility" was limited to the bounds of that function. This behavior is actually utilized to simulate private properties or methods (even in ES6, internally the same happens). So no wonder that the function is not accessible from outside.

Now to the part "how the this keyword works":

this always accesses the property of the object that is bound to that keyword. You can bind any object to a function using the function.bind method, inside that function, this is bound to that object. Also, you can apply a function onto a specific object using function.bind but in general, this is done for you when using the new keyword like this:

var x = new Coin( 3 );

new creates an object and applies the function Coin to it. That new object will later be assigned to the variable called x (later means: after the constructor has finished executing).

While in the constructor, you have access to that newly object using this. When you want to create a function as a method of that object, you need to assign it to a property like this:

this.methodName = function( param ){ ... };

By doing so, you assign a closure (anonymous function) to a property, and that makes it a method.

This allows you to call that method later on any object that has been created by your constructor:

x.methodName( paramValue );

Every object has a so-called prototype object. That is in general nothing else than a "fallback" to the lookup for methods and properties: If a method or property that you try to access is not found on your object itelf, then its prototype object is being searched for that identifier. This object has a prototype as well, so the lookup continues until the topmost parent has been searched. If the propery still is not found, it is undefined.

Every function has a property called prototype: This is not the prototype object of the function itself, but the prototype object that gets assigned to every object created by this (constructor-)function (using new). So, by assigning these functions as properties to the constructor prototype, you don't need to add the same methods to every created object again and again (which means, every created Coin-object would have its own getCents-Method => memory intensive), but you define the method only once, assign it to the prototype object which is the same for every Coin-object created with new Coin( n ) (as long as you don't change the prototype object, which you should not do for optimization reasons).

Psi
  • 5,467
  • 2
  • 14
  • 24
  • `this.setCents(intCents)`, also please add an explanation as to why. – Andrew Li Mar 28 '17 at 00:32
  • As to my understanding, prototype is meant to add new things to an object if it needs additional functionality. Why should I use prototype if I know the object will require the setCents and getCents in the first place? – Tyler Mar 28 '17 at 00:38
  • I will explain the prototype chain in my answer as well – Psi Mar 28 '17 at 00:39
  • Thanks for the clarification on the prototype chain, that is what I what missing in my debugging. :) – Tyler Mar 28 '17 at 00:57
1

This is probably a duplicate of How does the “this” keyword work?

A function's this is usually set by the call (bind and arrow functions are exceptions, but they aren't relevant here) and is scoped to the current execution context (except for arrow functions, which adopt the this of their immediate outer context).

So given the setCents function within the constructor is called without setting its this:

function setCents(intValue) {
    if (isValid(intValue)) {
        this.intCents = intValue; //---javascript bugs out here!---
    } else {
        console.error("Invalid value given to Coin Object.");
        this.intCents = NaN;
    }
}

setCents(intValue);

then its this will default to the global object (or remain undefined in strict mode). It won't be the new instance.

The getCents and setCents methods are not accessible from outside the constructor, so likely they should be on the constructor's prototype so they are inherited by each instance, e.g.

function Coin(intValue) {
  this.intCents = null; //The property
  // Initialise cents
  this.setCents(intValue);
}

Coin.prototype.setCents = function(intValue) {
    // isValid not provided so commented out
//  if (isValid(intValue)) {
    this.intCents = intValue; 
//    } else {
//    console.error("Invalid value given to Coin Object.");
//    this.intCents = NaN;
//  }
}

Coin.prototype.getCents = function() {
  return this.intCents;
}

var aCoin = new Coin(25);
console.log(aCoin.getCents()) // 25
aCoin.setCents(50);
console.log(aCoin.getCents()) // 50
Community
  • 1
  • 1
RobG
  • 124,520
  • 28
  • 153
  • 188
  • Along with Psi's answer, I was misunderstanding the own prototype concept. Thanks for the clarification! – Tyler Mar 28 '17 at 00:59
  • 1
    @Tyler—the new(ish) [*class* syntax](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Classes) makes things much tidier, but unfortunately there are still many browsers in common use that don't support it (such as any version of IE and Opera). You can also use *get* and *set*, which also helps. – RobG Mar 28 '17 at 01:13
  • yes that syntax looks a lot neater. I might see it in my college classes soon, we only recently started looking into javascript classes and objects. – Tyler Mar 28 '17 at 01:42
  • The class syntax, however, is just syntactic sugar. Everything possible with the new ES6 syntax constructs can be done with ES5. You can for example define getters and setters in ES5 by using Object.defineProperty – Psi Mar 28 '17 at 18:22