为什么开发者在JavaScript中使用get和set时使用“_”?

Why do developers use "_" while using get and set in JavaScript?

我知道在JavaScript中使用下划线只是定义私有变量的约定。但是我遇到了一个用例 [在使用 class] 时似乎必须使用 _ 才能使代码正常工作!我的问题是 getset 如何在幕后使用 _

以下代码会引发错误:

RangeError: Maximum call stack size exceeded

class User {
  constructor(name) {
    this.name = name;
  }

  get name() {
    return this.name;
  }

  set name(val) {
    this.name = val;
  }
}

let user = new User("Jhon");
console.log(user.name);

现在,如果我使用 _,代码就可以工作了!

class User {
  constructor(name) {
    this.name = name;
  }

  get name() {
    return this._name; // Added "_" here
  }

  set name(val) {
    this._name = val; // Added "_" here
  }
}

let user = new User("Jhon");
console.log(user.name);

您的第一个代码段对 getter/setter 使用了与您尝试分配给的 属性 相同的名称。因此,在构造函数中,当您执行

this.name = name;

您正在调用 name setter,它会:

this.name = val;

再次调用 name setter,递归调用自身直到堆栈溢出。

实际 属性 数据存储使用不同的变量名称(与 getters/setters 相比)允许代码作为故意的。它不必以下划线作为前缀 - 除了 getters/setters 使用的相同名称外,几乎 任何 都可以。

class User {
  constructor(name) {
    this.name = name;
  }

  get name() {
    return this.actualProperty;
  }

  set name(val) {
    this.actualProperty = val;
  }
}

let user = new User("Jhon");
console.log(user.name);

属性 名称前的 _ 通常表示 属性 是私有的,只有 class 本身可以访问它,但这并不能保证 - class 的用户仍然可以根据需要自由参考 user._name。如果你想要每个实例的 actual 私有数据,你应该在一个包含私有数据的 WeakMap 的闭包中定义 class:

const User = (() => {
  const data = new WeakMap();
  return class User {
    constructor(name) {
      this.name = name;
    }

    get name() {
      return data.get(this);
    }

    set name(val) {
      data.set(this, val);
    }
  }
})();

let user = new User("Jhon");
console.log(user.name);

逻辑上看这段代码:

get name() {
    return this.name
}

您阅读了object.name。对于 return 一个值,get name() getter 读取 this.name,然后解析为 get name()。现在,欢迎来到无限循环。

因此,您需要一个不同于getter 的名称的单独变量名称(用于存储name 的实际内容)。那将是一个私有变量,在这些情况下,在前面加上下划线已成为一种惯例。

_ 词缀通常用于私有属性。

当您希望能够控制更新 属性 的方式和时间,或者为这些操作添加副作用时,除了 getter and/or setter 之外,您还可以使用私有属性。

您还应该在 class

中有一个私有的 属性 声明
class User {
  private _name; // <-- here

  constructor(name) {
    this.name = name;
  }

  get name() {
    return this._name;
  }

  set name(val) {
    this._name = val;
  }
}