JS中的getter和setter你会用吗?

2,707 阅读3分钟

「这是我参与11月更文挑战的第14天,活动详情查看:2021最后一次更文挑战

对象属性有两种类型,我们平时使用到的都叫做数据属性;还有一种,访问器属性(accessor properties),它们本质上是用于获取和设置值的函数,但从外部代码来看就像常规属性。

“getter” 和 “setter”

例如,我们有一个具有 namesurname 属性的对象 user

let user = {
  name: "John",
  surname: "Smith"
};

如果你现在还想添加一个 fullName 属性,该属性值由name和surname组成,你可以这样做

image-20211114235400705

上面我们已经能获取到user.fullName的值了,之所以能正常 读取 :就是因为有getter 在幕后运行。但是当你尝试给这个新属性赋值时

image-20211114235803160

你会发现报错了,因为当前的fullName 只有一个 getter。(注意,如果不是严格模式,不会提示这个错误);

所以你如果需要赋值,那么就应该对应的在加上一个setter

let user = {
  name: "John",
  surname: "Smith",

  get fullName() {
    return `${this.name} ${this.surname}`;
  },

  set fullName(value) {
    [this.name, this.surname] = value.split(" ");
  }
};

现在可以在来试试结果

image-20211115000226065

这样,就实现了一个可读且可写的“虚拟”属性。


访问器描述符

我们前面已经说过属性的描述符是什么,那么【访问器属性】的描述符与【数据属性】的描述符一样吗?

答案是:不。

对于访问器属性,没有 valuewritable,但是有 getset 函数。

所以访问器描述符可能有:

  • get —— 一个没有参数的函数,在读取属性时工作,
  • set —— 带有一个参数的函数,当属性被设置时调用,
  • enumerable —— 与数据属性的相同,
  • configurable —— 与数据属性的相同。

“getter” 和 “setter” 的高级应用

你还可以把getter/setter 用作“真实”属性值的包装器,进行值的限制。

示例1:假如你想禁止太短的 user 的 name,可以创建一个 setter name,并将值存储在一个单独的属性 _name 中,即

let user = {
  get name() {
    return this._name;
  },

  set name(value) {
    if (value.length < 4) {
      console.log("名字太多了,至少4个字符");
      return;
    }
    this._name = value;
  }
};

这个时候你在对name进行设置

image-20211115001206143

可以看到,只有满足条件的才可以设置成功。

示例中name 被存储在 _name 属性中,并通过 getter 和 setter 进行访问。从技术上讲,外部代码可以使用 user._name 直接访问 name。但是,这儿有一个众所周知的约定,即以下划线 "_" 开头的属性是内部属性,不应该从对象外部进行访问。

你还可以利用 getter 和 setter 替换“正常的”数据属性,来控制和调整这些属性的行为。

示例2:假如你开始是使用数据属性 nameage 来实现 user 对象

image-20211115001723102

后面你对象里又不想要age了,想要改成birthday

image-20211115001926845

这个时候存在一个问题,之前用到age属性的地方怎么办?

你可以找到所有的age,然后修改它,但是很明显这太麻烦了。这个时候你就可以使用getter来优雅的解决

function User(name, birthday) {
  this.name = name;
  this.birthday = birthday;

  // 年龄是根据当前日期和生日计算得出的
  Object.defineProperty(this, "age", {
    get() {
      let todayYear = new Date().getFullYear();
      return todayYear - this.birthday.getFullYear();
    }
  });
}

现在你不但成功添加了birthday,同时age也仍然可以正常访问。

image-20211115002240780

总结

getter

get语法将对象属性绑定到查询该属性时将被调用的函数。

语法:

{get prop() { ... } }

{get [expression]() { ... } }

getter 使用场景

  • 需要允许访问返回动态计算值的属性。

  • 需要反映内部变量的状态,而不需要使用显式方法调用。

setter

当尝试设置属性时,set语法将对象属性绑定到要调用的函数。

语法:

{set prop(val) { . . . }}
{set [expression](val) { . . . }}

setter 使用场景

  • 试着改变一个伪属性的值。(就像我们上面示例中的fullName)

参考资料:

MDN getter

MDN setter

Property getters and setters


🎨【点赞】【关注】不迷路,更多前端干货等你解锁

往期推荐

👉 产品、技术、设计等各互联网领域的「基础术语」扫盲

👉 Web安全的防御手段都在这里了!

👉 JavaScript的7大类型补缺补漏!

👉 JavaScript深拷贝和浅拷贝看这篇就够了!