【问题标题】:Are `getter` and `setter` necessary in JavaScript?JavaScript 中是否需要 `getter` 和 `setter`?
【发布时间】:2021-12-30 02:02:15
【问题描述】:
class Employee {

    constructor(name, age) {
        this._name = name;
        this.age = age;
    }

    doWork() {
        return `${this._name} is working`;
    }

    get name() {
        return this._name.toUpperCase();
    }

    set name(newName){
        if(newName){ 
            this._name = newName;
        }
    }
}

let man = new Employee('A', 10);
console.log(man.name, man.age);
man.name = 'B';
man.age = 20;
console.log(man.name, man.age);

这是我的代码。我为_name 成员创建了gettersetter。我没有为age 创建gettersetter

但是两者都可以像这样man.name = 'B';man.age = 20;

更新这两个字段

所以我很困惑,gettersetter 在 JavaScript 中是必需的吗?

【问题讨论】:

  • 是的,它们有时是必要的。如果您的代码中不需要它们,请不要使用它们。
  • 从角度来看,这个问题在 this question 中针对 Java 得到了回答。有些方面是相似的。

标签: javascript


【解决方案1】:

gettersetter 在 JavaScript 中是必需的吗?

必要是一个未定义的词。任何问题都可以在没有 getter 和 setter 的情况下解决(通常通过将接口更改为方法而不是直接访问属性),就像任何问题都可以在没有 for 循环的情况下解决(通过替换 while 循环和手动维护的循环变量),但两者都是编程语言中有用的特性,因为当它们与用例匹配时,它们是更清洁、更方便的编程方式。因此,它们是“有用的”和“有帮助的”。

gettersetter 有时非常有用,但仅在需要其特定功能时才需要使用它们 - 否则纯属性访问没有 getter 或 @ 987654329@就可以了。

当您希望在每次请求属性时运行一些代码时,getter 很有用。在您的示例中,getter 始终返回名称的大写版本,无论名称的大小写如何,同时保留实际大小写以供内部使用。

当您想在每次设置属性时运行一些代码时,setter 很有用。在您的情况下,它可以防止设置虚假名称。如果没有getter/setter,您将无法实现其中任何一个功能。

当您不需要 gettersetter 特殊逻辑时,直接访问属性是一种非常好的方法。

getterssetters 也有可能获取和设置一些不公开可用的属性(因此根本无法通过正常的属性访问),或者因为它存储在不同的地方(不是作为属性此对象)或私下存储,甚至存储在其他位置,例如某些硬件设备中。在这些情况下,gettersetter 模拟属性中的值,而实际上它不是。因此,它们简化了界面,同时允许从任何地方存储或检索实际值。

【讨论】:

  • @tera_789 - 可以用基本功能“做什么”吗? OP 可以创建两个常规方法,称为 getName()setName(),但这将是一个与在属性接口后面使用 setter 和 getter 不同的接口。如果您希望有一个虚拟属性可以访问属性接口,则只能使用 setter 和 getter 来实现。否则,您可以访问获取和设置的方法,并且可以使用普通方法完成这些操作。因此,这取决于您希望对象具有的接口类型。
  • @tera_789:你的问题很好,已经被问到here
  • @Plee - 所以你认为每个对象的每个属性都只能通过 getter 和 setter 访问?真的吗?为什么不只在有好处/需要时才通过 getter/setter 访问给定的属性?您可以随时添加 getter/setter(当有实际理由需要时),因为它根本不会改变访问属性的方式。同时,将事物保留为不需要 getter/setter 的普通属性,可以保留普通属性的性能优化,并使您的代码更简单,这都是好事。
  • @Plee - 另外,将 Javascript 与 Java 进行比较不会将您引向一个好的方向。它们是不同的语言,具有不同的设计工具,影响性能的不同事物,不同的目标等等......当我从 C++ 毕业后第一次学习 Javascript 时,由于我的 C++ 背景,我用 Javascript 做了各种各样的事情,我最终后悔了.人们需要了解新语言是什么以及如何最好地用该语言编写代码,而不是尝试从另一种语言中获取设计意见并假设它们适用于 Javascript。
  • @Plee - 我不确定你在问什么。我认为这超出了我们可以在 cmets 中正确执行的范围。如果您想进一步追求这一点,我建议您针对您在上次评论中所说的内容提出自己的问题。您可以在评论中将问题的链接放在此处,我会看一看。
【解决方案2】:

有必要吗?没有。

是否存在最适合表示为 getter/setter 的场景?是的。考虑“计算属性”:

//declare an object
rectangle = {
   x: 10,
   y: 20,
   get area() { return this.x * this.y }  //won't occupy storage
}

//usage
log('the area is', rectangle.area)        //=> 'the area is 200'.
                                          //   cleaner syntax (no parentheses)

考虑 API 设计人员的需求 - 他们很可能对用户如何看待他们的 API 非常敏感。每一位(或者在这种情况下,括号)都计入清理界面。

【讨论】:

  • 在您的示例中,除了需要调用括号之外,此方法是否与 getter 相同? area: () => { return this.x * this.y }
  • 一模一样
【解决方案3】:

是的,它们是非常必要的。只是不是每个属性。

拥有setter的原因有两个:

1。您需要验证传入的值。

假设您的值必须是 0 到 100 之间的整数。您的 setter 可以验证传入值的类型是否正确且在正确的范围内:

class Range {
  set value(newVal) {
    let val = Number(newVal);
    
    if( newVal == null || typeof newVal === 'string' || !Number.isInteger(val) || val < 0 || val > 100 ) {
      const err = `'value' must be an integer from 0 to 100 and not ${newVal}`;
      console.error(err);
      //throw new TypeError(err);
    }
    
    // save newVal
  }
}

const x = new Range();
x.value = 200;
x.value = 10;
x.value = "10";
x.value = new Number(22);

2。每次值发生变化时你都需要做一些事情

class ValOutput {
  constructor(el) {
    this._el = el;
  }

  set value(newVal) {
    this._el.innerHTML = `The value is ${newVal}`;
  }
}

const output = document.getElementById('output');
const x = new ValOutput(output);
x.value = 100;
setTimeout(() => {
  x.value="after timeout";
}, 2000);
&lt;div id="output"&gt;&lt;/div&gt;

拥有 getter 的原因有两个:

1。计算值

class Rect {
  constructor(l = 0,t = 0,r = 0,b = 0) {
    this.left = l;
    this.top = t;
    this.right = r;
    this.bottom = b;
  }
  
  get height() {
    return this.bottom - this.top;
  }

  get width() {
    return this.right - this.left;
  }
}

let a = new Rect(0, 10, 33, 55);
console.log(a.width, a.height);

 a = new Rect(35, 50, 200, 200);
console.log(a.width, a.height);

2。您正在代理另一个对象的值

class OtherThing {
  constructor(a) {
    this.a = a;
  }
}

class MyObj {
  constructor(oVal = 0) {
    this._otherThing = new OtherThing(oVal);
  }
  
  get a() {
    return this._otherThing.a;
  }
}

const x = new MyObj();
console.log(x.a);

const y = new MyObj('tacos');
console.log(y.a);

隐藏私有变量

getterssetter 是隐藏私人数据的好方法。是的,我知道 ES 规范正在引入私有变量,但这里有一个示例,直到该规范成为标准。

const privateVars = new WeakMap();

class MyObj {
  constructor(inVal) {
    const pVars = {
      age: inVal,
      dog: ''
    }
    
    privateVars.set(this, pVars);
  }
  
  get dog() {
    return privateVars.get(this).dog;
  }
  set dog(newVal) {
    privateVars.get(this).dog = newVal;
  }
}

const x = new MyObj(10);
const y = new MyObj(20);


x.dog = "woof";
y.dog = "bark";

console.log(x.dog);
console.log(y.dog);

您需要gettersetters 吗?不,它们有必要吗?是的。

【讨论】:

    【解决方案4】:

    实现 getter 和 setter 功能的主要原因之一是缩小功能差距,该差距需要人们破解 js 解释器/浏览器来实现浏览器可以执行的一项功能:

    element.innerHTML = "<div> some HTML string </dif>";
    

    现在,innerHTML 可能看起来像一个属性,但实际上它的行为更像一个函数。它实际上是一个 HTML 编译器。

    你可以通过尝试看到这一点:

    element.innerHTML += "<table>";
    element.innerHTML += "<tr><td>test</td></tr>"; // does not work
    

    这是因为第一次“调用”innerHTML 会将 html 编译为:

    <table></table>
    

    第二行将失败,因为表外的&lt;tr&gt; 是无效的 HTML。

    使用 getter 和 setter,您终于可以在纯 javascript 中实现这样的功能 - 使属性访问触发函数调用。

    当然,innerHTML 只是 getter 和 setter 的一个例子,也是一个非常糟糕的 API 例子。您可以用 getter 和 setter 实际做什么取决于您的创造力。

    现在,就我个人的观点而言(这只是我的观点,所以请看它的价值):我认为innerHTML 提供了一个很好的例子,说明为什么通常不应使用 getter 和 setter。对&lt;table&gt; 的困惑是打破用户期望的一个很好的例子。如果 innerHTML 被实现为 element.appendHTML() 它的行为就不会那么令人惊讶了。作为一名程序员,如果属性访问做了一些我没有预料到的事情,我会感到非常惊讶。

    也就是说,我很高兴 getter 和 setter 的存在使语言库功能可以自行实现,而无需求助于 C/C++ 中的 hack。

    【讨论】:

      【解决方案5】:

      我们使用类的原因是它包含单个实体的所有属性。但是您可以通过创建一个对象来完成,并且可以在需要时将属性附加到该对象。

      当代码变大时,您将无法调试代码失败的地方。如果您遵循该结构,则可以轻松地进行调试。因为每个实体都将位于单独的文件中,并且逻辑将是分开的。

      但是 JavaScript 是一种面向对象的编程语言。我们使用类而不是对象的原因,对于 getter 和 setter 来说也是一样的。它不像你可以在不需要的地方使用。这是一个很好的做法通过 setter 和 getter 设置和获取所有属性。

      在某个时间点之后,您可能会在设置属性时添加一些条件,例如如果一个人的年龄大于 100,您不应该在属性中设置。那时您需要更改基类,以及在您的孩子课程中。这将是我们生活中最困难的部分(代码重构)。因此,请避免此类问题并确保您的变量安全,从我的角度来看,我们应该使用 getter 和 setter。抱歉,答案很长

      【讨论】:

        【解决方案6】:

        使用gettersetter 方法取决于您在类中的属性,如果您想将属性设置为只读,则使用getter 方法如下:

        function Person(name, age){
          var _name = name;
          this.age = age;
          
          this.getName = function(){
            return _name;
          }
        }
        
        var teacher = new Person('Sara', 24);
        
        //now you can just get the name of teacher 
        alert(teacher.getName());
          

        【讨论】:

        • 你可以通过添加一个像setName这样的新方法来做到这一点,所以这只是一种改变_namees3样式代码的方法。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-12-29
        • 2011-02-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多