深入了解对象属性 getters 与 setters

人生代码

共 5586字,需浏览 12分钟

 ·

2021-03-23 19:16

Property getters and setters

有两种对象属性。

第一种是数据属性。我们已经知道如何与他们合作。到目前为止,我们使用的所有属性都是数据属性。

第二种类型的属性是新的东西。访问属性。它们本质上是在获取和设置值时执行的函数,但对于外部代码来说,它们看起来像常规属性。

Getters and setters

访问器属性由“getter”和“setter”方法表示。在对象字面量中,它们由get和set表示:

let obj = {
  get propName() {
    // getter, the code executed on getting obj.propName
  },

  set propName(value) {
    // setter, the code executed on setting obj.propName = value
  }
};

getterobj.propName被读取,setter -当它被分配。

例如,我们有一个带有name和姓氏的user对象:

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

现在我们要添加一个fullName属性,它应该是“John Smith”。当然,我们不想复制粘贴现有的信息,所以我们可以将其作为访问器来实现:

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

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

alert(user.fullName); // John Smith

从外部看,访问器属性与普通属性类似。这就是访问器属性的思想。我们不叫用户。作为一个函数,我们通常读取它:getter在后台运行。

到目前为止,fullName只有一个getter。如果我们试图分配user.fullName=,会有一个错误:

let user = {
  get fullName() {
    return `...`;
  }
};

user.fullName = "Test"// Error (property has only a getter)

让我们通过为user.fullName添加setter来解决这个问题:

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

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

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

// set fullName is executed with the given value.
user.fullName = "Alice Cooper";

alert(user.name); // Alice
alert(user.surname); // Cooper

访问器描述符

访问器属性的描述符与数据属性的描述符不同。

对于访问器属性,没有值或可写,而是有get和set函数。

也就是说,访问器描述符可能有:

  • get——一个没有参数的函数,在读取属性时工作,

  • set -一个有一个参数的函数,当属性被设置时被调用,

  • enumerable -与数据属性相同,

  • configurable -与数据属性相同。

例如,要使用defineProperty创建访问器的全名,可以使用getset传递描述符:

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

Object.defineProperty(user, 'fullName', {
  get() {
    return `${this.name} ${this.surname}`;
  },

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

alert(user.fullName); // John Smith

for(let key in user) alert(key); // name, surname

请注意,一个属性可以是访问器(有get/set方法)或数据属性(有值),而不是两者都有。

如果试图在同一个描述符中同时提供getvalue,则会出现错误:

// Error: Invalid property descriptor.
Object.defineProperty({}, 'prop', {
  get() {
    return 1
  },

  value2
});

更加智能的 Smarter getters/setters

getter /setter可以被用作“真实”属性值的包装器,以获得对使用它们的操作的更多控制。

例如,如果我们想禁止对用户使用太短的名称,我们可以有一个setter名称,并将值保留在单独的属性_name中:

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

  set name(value) {
    if (value.length < 4) {
      alert("Name is too short, need at least 4 characters");
      return;
    }
    this._name = value;
  }
};

user.name = "Pete";
alert(user.name); // Pete

user.name = ""// Name is too short...

名字存储在_name属性中,访问是通过gettersetter完成的。

从技术上讲,外部代码可以通过user._name直接访问该名称。但有一个众所周知的约定,即以下划线“_”开头的属性是内部的,不应该从对象外部接触。

使用的兼容性

访问器的一个重要用途是,它们允许随时控制“常规”数据属性,方法是用gettersetter替换它,并调整其行为。

假设我们开始使用数据属性nameage来实现用户对象:

function User(name, age{
  this.name = name;
  this.age = age;
}

let john = new User("John"25);

alert( john.age ); // 25

但事情迟早会改变。我们可能会决定存储生日而不是年龄,因为它更精确和方便:

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

let john = new User("John"new Date(199261));

那么,如何处理仍然使用年龄属性的旧代码呢?

我们可以尝试找到所有这些地方并修复它们,但这需要时间,而且如果代码被许多人使用的话,就很难做到这一点。此外,年龄对于用户来说是件好事,对吧?

让我们保持它。

为年龄添加getter可以解决这个问题:

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

  // age is calculated from the current date and birthday
  Object.defineProperty(this"age", {
    get() {
      let todayYear = new Date().getFullYear();
      return todayYear - this.birthday.getFullYear();
    }
  });
}

let john = new User("John"new Date(199261));

alert( john.birthday ); // birthday is available
alert( john.age );      // ...as well as the age


浏览 43
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报