🔦 JavaScript 对象的访问器属性和描述符
对象有两种 对象属性:
- 数据属性。常用存储于对象的数据的属性。
- 访问器属性(accessor property)。本质上是用作 获取和设置值的函数,但是从外部看像常规属性。
# getter
/ setter
在 对象字面量 中,使用 get
和 set
表示:
let obj = {
get propName() {
// ... 当读取 obj.propName 时起作用
},
set propName() {
// ... 设置 obj.propName 的值时起作用
}
}
2
3
4
5
6
7
8
🌰 例子:
let user = {
name: "John",
surname: "Smith",
get fullName() {
return `${this.name} ${this.surname}`;
}
};
console.log(user.fullName) // John Smith
2
3
4
5
6
7
8
9
10
从外部看访问属性就像一个普通的属性,这就是 访问属性的设计思想。不应该使用 函数形式 访问。只要正常读取 访问属性,而 getter
在幕后运行。
在上面的例子中,如果不设置 setter
,此时对 fullName
设值(赋值),就会出错。所以就要给 fullName
添加一个 setter
:
let user = {
name: "John",
surname: "Smith",
get fullName() {
return `${this.name} ${this.surname}`;
},
set fullName(value) {
[this.name, this.surname] = value.split(" ");
}
};
// 此时赋值
user.fullName = "Alice Cooper"
console.log(user.name) // Alice
console.log(user.surname) // Cooper
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
至此, fullName
访问属性可以看作是一个 「虚拟属性」,可读且可写。
# 访问器描述符
访问器属性的描述符与数据属性的不同。对于访问器属性,没有 value
和 writable
,但是有 get
和 set
函数。
访问器描述符有:
get
:一个 没有参数 的 函数,在读取属性时工作。set
:带有 一个参数 的 函数,当属性被设置时调用。enumerable
:与数据属性的相同。configurable
:与数据属性的相同。
🌰 例子 / 使用 defineProperty
创建一个 fullName
的访问器。可以使用 get
和 set
传递描述符。
let user = {
name: "John",
surname: "Smith"
};
Object.defineProperty(user, 'fullName', {
get() {
return `${this.name} ${this.surname}`;
},
set(value) {
[this.name, this.surname] = value.split(" ");
}
});
console.log(user.fullName) // John Smith
for(let key in user) alert(key); // name, surname
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
注意
一个属性要么是访问器(具有 get/set
方法),要么是数据属性(具有 value
),但不能两者都是。如果试图在同一个描述符中同时提供 get
和 value
,就会报错。
getter
和 setter
可以用于包装 真实 属性值(包装器),可以对它们实现更多的控制。
🌰 例子:
let user = {
get name() {
return this._name
}
set name(value) {
if(value.length < 4) {
console.log("")
}
this._name = value
}
}
user.name = "Pete"
console.log(user.name)
user.name = "A" // 太短
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
name
被存储在_name
属性中,并通过getter
和setter
进行访问。从技术上讲,外部代码可以使用
user._name
直接访问 name。但是,这儿有一个众所周知的约定,即以下划线"_"
开头的属性是内部属性,不应该从对象外部进行访问。
# 访问器的兼容性
访问器的一大用途是,它们允许随时通过使用 getter
和 setter
替换「正常的」数据属性,来控制和调整这些属性的行为。
🌰 例子:
function User(name, age) {
this.name = name;
this.age = age;
}
let john = new User("John", 25);
2
3
4
5
6
但是,之后可能会选择 存储 birthday
而不是 age
,选择日期类型的 birthday
可以更加精准,但是 age
又想要保留:
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();
}
});
}
let john = new User("John", new Date(1992, 6, 1));
2
3
4
5
6
7
8
9
10
11
12
13
14