Skip to main content
 首页 » 编程设计

Javascript面向对象(之setter、getter属性

2022年07月19日116telwanggs

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); // Alice 
alert(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 Smith 
 
for(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); // Pete 
 
user.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 available 
alert( john.age );  // ...as well as the age 

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


本文参考链接:https://blog.csdn.net/neweastsun/article/details/70186682