Javascript面向对象(二)——setter、getter属性

来源:互联网 发布:三维编程叫什么 编辑:程序博客网 时间:2024/06/04 08:17

Javascript面向对象(二)——setter、getter属性

Javascript对象有两种属性,一种是数据属性,我们经常使用比较熟悉;第二种是访问器属性,本质就是获取和设置值的函数,但从代码上好像是正常属性。

Getters 和 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  }};

obj.proName调用是,getter方法读取属性,赋值时setter方法调用。示例,我们有user对象,有namesurname两个属性。
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

从外面看,访问器属性好像正常属性,这是访问器属性的思想。我们没有作为一个函数调用user.fullName,只是正常读取:getter在后台运行。
现在,fullName仅有一个getter,如果我们打算赋值user.fullName=,则会报错。让我们来增加setter给user.fullName修改错误。

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); // Alicealert(user.surname); // Cooper

现在我们有了一个虚拟的属性,可读可写,实际上并不存在。

访问器属性只能使用get/set来访问

属性可以是数据属性或访问器属性,但不能都是。
一旦属性被定义了get prop()或在set prop(),则为访问器属性。所以必须通过getter读取、setter赋值。
有时仅有setter或getter,这时正常的,只是这时不能读取或赋值。

访问器描述符

访问器描述符是不同的。对访问器属性,没有valuewritable,代替他们的是getset函数。
访问器描述可能有:

  • get – 无参函数,读取属性时调用,
  • set – 无参函数, 给属性赋值时调用,
  • enumerable – 与数据属性一致,
  • configurable – 与数据属性一致.

示例,使用defineProperty创建fullName访问器时,可以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 Smithfor(let key in user) alert(key);

再次提醒,属性只能是访问器或数据属性,不能都是。如果我们视图给描述符同时提供getvalue,会报错:

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

智能的getter/setter

getter/setter能用作“真实”属性的包装器,实现更多的控制。例如,如果我们想避免user的名称太短,可以存储name在一个特殊属性_name中,然后使用setter过滤赋值。

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); // Peteuser.name = ""; // Name is too short...

技术上,外部代码可以直接访问user._name,但有个普遍的共识,使用“_”开头的属性,仅在内部使用,外部对象不要直接访问。

兼容性用途
getter和setter的一个好处是,他们可以控制正常数据属性,并在任何时候调整。举例,我们开始实现user对象使用数据属性nameage

function User(name, age) {  this.name = name;  this.age = age;}let john = new User("John", 25);alert( john.age ); // 25

但是很快或后来,需求变了,代替age,我们可能决定去存储birthday,因为更严格、方便:
function User(name, birthday) {
this.name = name;
this.birthday = birthday;
}

let john = new User("John", new Date(1992, 6, 1));

现在,如何出来原来的代码,他们仍然在使用age属性?
我们可以尝试找到所有age的代码并修改他们,但这太费时,且如果代码是别人写的,很难去做。另外name属性对user对象来说并不多余,在有些地方正是我们需要的,给age增加一个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(1992, 6, 1));alert( john.birthday ); // birthday is availablealert( john.age );  // ...as well as the age

现在我们多了一个属性,而且原来的代码也正常工作。

1 0