Javascript setter 导致无效

发布于 2025-01-10 20:56:21 字数 1558 浏览 2 评论 0原文

我正在做一个项目,但我的设置者只是拒绝工作。我尝试了多种解决方案,例如将其设为私有(但这不起作用,因为我需要访问它),将设置器下的值重命名为不同的值,并尝试使用或不使用括号来调用它们。

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);
    }

这是我的汽车类,我正在其中创建构造函数和 getter setter。与其他方法

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)
    ];

上面的 Car 对象数组位于 Car 类之外。

看看 Car 类中的 move 方法。我稍后在代码中使用它,如下所示:

cars[2].move;
let str = cars[2].getHPosition;
console.log(str);

应该调用 cars[2] 上的 setter 并将其 HPosition 设置为 10 相反,它返回 0,就好像 setter 从未被调用过一样。我知道这里一定有一些愚蠢的错误,但我对此很恼火

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

痴者 2025-01-17 20:56:21

您的错误

您的代码可能出现意外行为的多个原因:

  • 您引用了,但没有调用 car[2].move。像 car[2].move() 一样调用它!
  • move 中,您尝试调用实例方法,就好像它是静态方法一样!请参阅 this 而不是 Car
  • 您正在尝试调用 JS setter!不要调用它 (setHPosition(20)),而是为该属性分配一个值 (setHPosition = val)。
  • setvPosition 中,您将值分配给属性 vPosition1(注意 1)。

这些错误可能来自于对某些 JS 主题的误解。

JS 中的函数

JS 是一种函数式编程语言,这意味着函数被视为所谓的 first-阶级公民。因此,引用一个函数(此处:move)但不调用它是完全有效的。

在您的情况下,您实际上想要调用该函数,这是通过将括号中的参数列表附加到函数来完成的:move()(此处不传递任何参数)。

测试函数是否被调用的一个简单方法是在函数中添加一些 console.log,尽管 Chrome 也提供了一些调试工具(我个人从未使用过)。

构造函数

构造函数在调用时更多是例外。您可以通过在函数名称前添加关键字 new 来调用构造函数,然后可以像常规函数一样调用它:new SomeConstructor(1, 2, 3)

(大多数命名约定都将构造函数大写。)

另外,与常规函数不同,也可以通过省略参数列表来调用构造函数:new SomeConstructor。这相当于调用不带任何参数的构造函数:new SomeConstructor()

您的 move 函数不会作为构造函数调用,因此它必须有一个要调用的参数列表:move()

函数上下文

在对象上调用函数将使该对象成为函数的上下文。否则,将使用周围的上下文。在顶层(全局范围),globalThisnull 是上下文,具体取决于您处于“草率”模式还是严格模式:

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}

因此,我们不需要期望将 Car 对象传递给 move,而是可以使用 this

此外,参数 Car 会隐藏类 Car,因为它是在词法作用域链的较早位置找到的。

如果您希望在函数中保持两者均可访问,最好将参数命名为(小写)car。一般来说,最好坚持一种命名约定,以免让自己感到困惑。

实例与静态方法

JS 中的类同时定义了实例和静态方法,但静态方法只能在类上定义,而实例方法只能在实例上定义。

示例

考虑以下情况:

class Example {
  static staticMethod() {}
  instanceMethod() {}
}

staticMethod 仅在类对象 Example 上定义,但不在实例 new Example() 上定义:

Example.staticMethod(); // works
new Example().staticMethod(); // fails; staticMethod is undefined on instances

对于 instanceMethod 恰恰相反:它只在实例 new Example() 上定义,而不是在类对象 Example 上定义:

Example.instanceMethod(); // fails; instanceMethod is undefined on the class object
new Example().instanceMethod(); // works

Getters/setters

JS 的特点是get/set 关键字,导致指定的属性仅在特定情况下起作用:

  • 当名称相似的属性被调用时,会调用 get 属性函数访问(读取)。
  • 当分配相似名称的属性时,将调用 set 属性函数。

在这两种情况下,它们都像常规属性而不是方法一样使用:

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

一个对象可以有相同属性的 getter 和 setter(如上所示,对于 name)。但是当定义了 getter 或 setter 时,不能在同一个对象上定义同名的数据条目(这就是为什么我使用 _name)。

如您所见,这些不是 get/set 方法。 getter/setter 通常仅在访问/分配期间必须发生附加功能(例如缓存、延迟初始化),或者具有不可变的属性引用,或者......

结论

时才使用 getter/setter你的班级。此外,您的属性都不是私有的(在类中可用;例如:this.#aPrivateProperty),这就是为什么我什至不会使用 get/set 方法,而是直接访问属性。

另外,我个人喜欢在类定义中定义属性,而不仅仅是在构造函数中。然后,您的 IDE 应自动完成属性,从而可能减少 vPosition1 等拼写错误。

这是我如何编写代码的简化示例:

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);

但是,如果您仍然想使用原始代码的 getter/setter:

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);

Your mistakes

Multiple reasons why your code may behave unexpectedly:

  • You reference, but not call car[2].move. Call it like car[2].move()!
  • In move you are trying to call an instance method as if it was a static method! Refer to this instead of Car.
  • You are trying to call a JS setter! Instead of calling it (setHPosition(20)), assign a value to the property (setHPosition = val).
  • In setvPosition, you assign the value to the property vPosition1 (notice the 1).

The errors may come from misunderstanding certain JS topics.

Functions in JS

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.logs, though e.g. Chrome also offers some debugging tools (which I personally have never used).

Constructor functions

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().

Function context

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.

Instance vs static methods

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.

Example

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

Getters/setters

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 ...

Conclusion

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);

妳是的陽光 2025-01-17 20:56:21

我认为在这种情况下,我会完全删除对 getter/setter 的依赖,而只使用通常调用的简单类方法。这样 move 就可以毫无问题地调用 setHPosition

class Car {

  constructor(id, image, vPosition, wins, hPosition, finishedRace) {
    this.id = id;
    this.image = image;
    this.vPosition = vPosition;
    this.hPosition = hPosition;
    this.finishedRace = finishedRace;
    this.wins = wins;
  }

  getvPosition() {
    return this.vPosition;
  }

  setvPosition(vPos) {
    this.vPosition1 = vPos;
  }

  getHPosition() {
    return this.hPosition;
  }

  setHPosition(hPos) {
    this.hPosition = hPos;
  }

  move(val) {
    this.setHPosition(val);
  }

}

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)
];

cars[2].move(10);
console.log(cars[2].getHPosition());

I think in this case I would remove the dependency on getters/setters altogether, and just have simple class methods that you call normally. That way move can call setHPosition without any issues.

class Car {

  constructor(id, image, vPosition, wins, hPosition, finishedRace) {
    this.id = id;
    this.image = image;
    this.vPosition = vPosition;
    this.hPosition = hPosition;
    this.finishedRace = finishedRace;
    this.wins = wins;
  }

  getvPosition() {
    return this.vPosition;
  }

  setvPosition(vPos) {
    this.vPosition1 = vPos;
  }

  getHPosition() {
    return this.hPosition;
  }

  setHPosition(hPos) {
    this.hPosition = hPos;
  }

  move(val) {
    this.setHPosition(val);
  }

}

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)
];

cars[2].move(10);
console.log(cars[2].getHPosition());

断肠人 2025-01-17 20:56:21

您的 move 函数采用 Car 参数并移动汽车,而不是当前的类实例this。因此,您应该更改 move 函数以获取距离值,然后调用 this.setHPosition = this.getHPosition + distance,假设您希望将汽车从当前位置移动一定的位移,而不是移动到某个位置。固定位置。即:

move(distance) {
    this.setHPosition = this.getHPosition + distance; // or just use this.hPosition directly: this.hPosition += distance
}

请注意,getter 和 setter 是像属性一样访问的函数,因此您不使用括号,而只需分配给 setter 的属性,或读取 getter 的属性。它是语法糖,但如果你使用它,你就必须正确使用它!最好不要在 setter/getter 前面加上“get”和“set”前缀,因为这会使它们看起来像函数并且可能会造成混淆。

Your move function takes a Car parameter and moves that car rather than the current class instance this. So you should change the move function to take a distance value and then call this.setHPosition = this.getHPosition + distance, assuming you want to move the car by a displacement from its current position rather than to a fixed position. i.e.:

move(distance) {
    this.setHPosition = this.getHPosition + distance; // or just use this.hPosition directly: this.hPosition += distance
}

Note that getters and setters are functions that are accessed like properties, so you don't use the parentheses, but simply assign to the property for a setter, or read the property for a getter. Its syntactic sugar, but if you use it, you gotta use it right! It's probably better not to prefix the setters/getters with 'get' and 'set' since that makes them look like functions and could be confusing.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文