I am working on a project and my setters just refuse to work whatsoever. I have tried multiple solutions like making it private (but that doesn't work since I need to access it), renaming the value under setters to something different and trying to call them with or without parenthesis.
class Car {
constructor(id, image, vPosition, wins, hPosition, finishedRace) { //there is a chance I may need to remove the last 3 parms (wins,hpos and finished race)
this.id = id;
this.image = image;
this.vPosition = vPosition;
this.hPosition = hPosition;
this.finishedRace = finishedRace;
this.wins = wins;
}
get getvPosition() {
return this.vPosition;
}
/**
* @param {any} vPos
*/
set setvPosition(vPos) {
this.vPosition1 = vPos;
}
get getHPosition()
{
return this.hPosition;
}
set setHPosition(hPos)
{
this.hPosition = hPos;
}
move(Car) {
Car.setHPosition(10);
}
this is my car class where I am creating the constructor and getter setters. with other methods
const cars = [
new Car("Classic", "images/car1.png", 150,0,0,0),
new Car("Bug", "images/car2.png", 350,0,0,0),
new Car("Hatchback", "images/car3.png", 550,0,0,0),
new Car("Sedan", "images/car4.png", 750,0,0,0)
];
Above array of object Car which is just outside of Car class.
take a look on that move method in Car class. I am using it later in the code like this:
cars[2].move;
let str = cars[2].getHPosition;
console.log(str);
Which should call setter on cars[2] and make it set its HPosition to 10 Instead it returns 0 as if the setter was never called. I know there must be some silly mistake here but I am so annoyed by it
Multiple reasons why your code may behave unexpectedly:
car[2].move
. Call it like car[2].move()
!move
you are trying to call an instance method as if it was a static method! Refer to this
instead of Car
.setHPosition(20)
), assign a value to the property (setHPosition = val
).setvPosition
, you assign the value to the property vPosition1
(notice the 1
).The errors may come from misunderstanding certain JS topics.
JS is a functional programming language, which means that functions are treated as so-called first-class citizens. For that reason, referencing a function (here: move
) but not calling it is perfectly valid.
In your case, you actually want to call the function, which is done by appending an argument list in parentheses to the function: move()
(here, no arguments are passed).
An easy way to test whether your function has been called would be to litter it with some console.log
s, though e.g. Chrome also offers some debugging tools (which I personally have never used).
Constructor functions are more the exceptions when it comes to calling them. You call constructor functions by prepending the keyword new
to the function name, and can then call it like a regular function: new SomeConstructor(1, 2, 3)
.
(Most naming conventions have constructor functions capitalized.)
Alternatively—unlike regular functions—constructor functions can also be called by leaving out the argument list: new SomeConstructor
. This is equivalent to calling the constructor without any arguments: new SomeConstructor()
.
Your move
function is not called as a constructor, thus it has to have an argument list to be called: move()
.
Calling a function on an object will make the object the function's context. Otherwise, the surrounding context is used. On the top level (global scope), either globalThis
or null
is the context, depending on whether you are in "sloppy" mode or strict mode:
console.log("Global scope:");
(function sloppyMode() {
console.log("-sloppy: this === globalThis?", this === globalThis);
})();
(function strictMode() {
"use strict";
console.log("-strict: this === undefined?", this === undefined);
})();
console.log(""); // Ignore; for whitespace in console
const compareThis = function(compTo) { return this === compTo; };
const obj = { compareThis };
console.log("Function scope:");
console.log("-direct call: this === globalThis?", compareThis(globalThis));
console.log("-direct call: this === obj?", compareThis(obj));
console.log("-on obj: this === globalThis?", obj.compareThis(globalThis));
console.log("-on obj: this === obj?", obj.compareThis(obj));
.as-console-wrapper{top:0;max-height:unset!important}
For this reason, we don't need to expect a Car
object to be passed to move
, but can instead use this
.
Also, the parameter Car
would shadow the class Car
since it is found earlier in the lexical scope-chain.
If you'd want to keep both accessible in the function, a good idea would be to name the parameter (lowercase) car
. Generally it would be good to stick to one naming convention to not confuse yourself.
A class in JS defines both instance and static methods, but static methods will only be defined on the class, and instance methods only on instances.
Consider the following:
class Example {
static staticMethod() {}
instanceMethod() {}
}
staticMethod
is only defined on the class object Example
, but not on instances new Example()
:
Example.staticMethod(); // works
new Example().staticMethod(); // fails; staticMethod is undefined on instances
For instanceMethod
it is the opposite: It is only defined on instances new Example()
, but not on the class object Example
:
Example.instanceMethod(); // fails; instanceMethod is undefined on the class object
new Example().instanceMethod(); // works
JS features the get
/set
keywords, which cause the specified property to only act in specific situations:
get
property functions are called when the property of similar name is accessed (read).set
property functions are called when the property of similar name is assigned to.In both cases they are used like regular properties instead of methods:
const object = {
_name: "my name",
get name() {
console.log("object:", "get name");
return this._name;
},
set name(v) {
console.log("object:", "set name");
this._name = v;
}
};
console.log("log:", object.name); // Uses getter
object.name = "another name"; // Uses setter
console.log("log:", object.name); // Uses getter
// Complex example:
// JS assigns right-to-left, so the expression evaluates to using the string,
// not to getting the property. Because of this, it only...
console.log("log:", object.name = "third name"); // Uses setter
// See https://262.ecma-international.org/12.0/#sec-assignment-operators-runtime-semantics-evaluation
An object can have a getter and setter for the same property (as can be seen above, for name
). But when a getter or a setter is defined, no data entry under the same name can be defined on the same object (which is why I instead used _name
).
As you can see, these aren't get/set methods. Getters/setters are usually only used when additional functionality has to happen during access/assignment (e.g. caching, lazy initialization), or to have immutable property references, or ...
I don't see a reason to use getters/setters in your class. Additionally, none of your properties are private (available in classes; example: this.#aPrivateProperty
), which is why I wouldn't even use get/set methods, but instead access the properties directly.
Also, what I personally like to do is define the properties in the class definition as well, instead of only in the constructor. Then your IDE should autocomplete to the properties, potentially reducing typos like your vPosition1
.
Here's a reduced example of how I would write your code then:
class Car {
hPosition;
vPosition;
constructor(hPos, vPos) {
this.hPosition = hPos;
this.vPosition = vPos;
}
move() { // We don't expect `Car`; we can use `this` instead!
this.hPosition = 10; // Shouldn't this be "+=" ?
}
}
const cars = [
new Car(150, 0),
new Car(350, 0),
new Car(550, 0),
new Car(750, 0)
];
cars[2].move(); // Don't forget to call!
console.log(cars[2].hPosition);
If you however still want to use the getters/setters of your original code:
class Car {
constructor(vPosition, hPosition) {
this.vPosition = vPosition;
this.hPosition = hPosition;
}
get getVPosition() { return this.vPosition; }
set setVPosition(vPos) { this.vPosition = vPos; } // Fixed typo vPosition1
get getHPosition() { return this.hPosition; }
set setHPosition(hPos) { this.hPosition = hPos; }
/* This *instance method* doesn't use *this instance* at all, so why is it not a static method?
* Also, instead of using *this instance*, it uses a passed-in instance. Why?
*/
move(car) {
car.setHPosition = 10;
}
}
const cars = [
new Car(150, 0),
new Car(350, 0),
new Car(550, 0),
new Car(750, 0)
];
cars[2].move(cars[2]); // We need to call the instance method, and then pass it the object to mutate.
let str = cars[2].getHPosition; // str now holds a number, but name suggests it holds a string.
str = String(str); // *Now* it holds a string.
console.log(str);