简而言之, gettersetter 访问器能让我们像使用属性一样使用方法。

getter

如果我们定义了一个对象,有 firstnamelastnamefullname 。 那么每次设置 firstnamelastname 时,同时去设置 fullname 就会很蠢。

$$jsdemo$$
$$edit$$
let person = {
    firstname: "艾伦",
    lastname: "耶格尔",
    fullname: "艾伦·耶格尔",
}

console.log(person) // {firstname: '艾伦', lastname: '耶格尔', fullname: '艾伦·耶格尔'}

// 修改 lastname 要同时修改 fullname
person.lastname = "耶路撒冷"
person.fullname = "艾伦·耶路撒冷"

console.log(person) // {firstname: '艾伦', lastname: '耶路撒冷', fullname: '艾伦·耶路撒冷'}

实际上我们的 fullname 可以由 lastname 得出,无需额外保存,已然出现了冗余。所以我们可以用 getFullname 方法代替 fullname

$$tip

数据库的设计中有设计三范式的概念,和这里的场景挺类似,说的就是数据不应出现冗余。

$$

$$jsdemo$$
$$edit$$
let person = {
    firstname: "艾伦",
    lastname: "耶格尔",
    getFullname() {
        return this.firstname + "·" + this.lastname
    },
}

alert(person.getFullname()) // 艾伦·耶格尔

// 修改 lastname 无需设置 fullname
person.lastname = "耶路撒冷"

alert(person.getFullname()) // 艾伦·耶路撒冷

getter 访问器就出现了,定义一个 getter 使用的是 get 关键字,我们能像访问一个属性一样使用方法。

$$jsdemo$$
$$edit$$
let person = {
    firstname: "艾伦",
    lastname: "耶格尔",
    get fullname() {
        return this.firstname + "·" + this.lastname
    },
}

// 注意这是一个属性,不用加括号
alert(person.fullname) // 艾伦·耶格尔

// 修改 lastname 无需设置 fullname
person.lastname = "耶路撒冷"

alert(person.fullname) // 艾伦·耶路撒冷

setter

fullname 这时只能获取值,有时我们也希望能直接通过赋值 fullname 来修改 firstnamelastname

$$jsdemo$$
$$edit$$
let person = {
    firstname: "艾伦",
    lastname: "耶格尔",
    get fullname() {
        return this.firstname + "·" + this.lastname
    },
    set fullname(value) {
        let names = value.split("·")
        this.firstname = names[0]
        this.lastname = names[1]
    },
}

// 注意这是一个属性,不用加括号
alert(person.fullname) // 艾伦·耶格尔

person.fullname = "艾伦·耶路撒冷"

alert(person.lastname) // 耶路撒冷

当然,一个校验也是不可少的。

$$jsdemo$$
$$edit$$
let person = {
    firstname: "艾伦",
    lastname: "耶格尔",
    get fullname() {
        return this.firstname + "·" + this.lastname
    },
    set fullname(value) {
        if (!value.includes("·")) {
            alert("请使用合法的 fullname")
        } else {
            let names = value.split("·")
            this.firstname = names[0]
            this.lastname = names[1]
        }
    },
}

// 注意这是一个属性,不用加括号
alert(person.fullname) // 艾伦·耶格尔

person.fullname = "艾伦耶路撒冷" // 请使用合法的 fullname

alert(person.lastname) // 耶格尔

练习

  1. 设计一个对象,能够处理以下操作。
let wedding = {
    // 补全这里
}

wedding.date = "2020年10月20日"

alert(
    `那是${wedding.year}年的时候,${wedding.month}月的天气有些寒冷,${wedding.day}号的下午我和你妈妈举行了婚礼。`
) // 那是2020年的时候,10月的天气有些寒冷,20号的下午我和你妈妈举行了婚礼。

$$answer 答案一:

赋值时直接使用 setter 访问器。

$$jsdemo$$
$$edit$$
let wedding = {
    year: null,
    month: null,
    day: null,
    set date(value) {
        let yearIdx = value.indexOf("年")
        let monthIdx = value.indexOf("月")
        let dayIdx = value.indexOf("日")

        this.year = parseInt(value.substring(0, yearIdx))
        this.month = parseInt(value.substring(yearIdx + 1, monthIdx))
        this.day = parseInt(value.substring(monthIdx + 1, dayIdx))
    },
    get date() {
        return `${this.year}年${this.month}月${this.day}日`
    },
}

wedding.date = "2020年10月20日"

alert(
    `那是${wedding.year}年的时候,${wedding.month}月的天气有些寒冷,${wedding.day}号的下午我和你妈妈举行了婚礼。`
) // 那是2020年的时候,10月的天气有些寒冷,20号的下午我和你妈妈举行了婚礼。

答案二:

获取值时再使用 getter 访问器。

$$jsdemo$$
$$edit$$
let wedding = {
    date: null,
    get year() {
        let yearIdx = this.date.indexOf("年")
        return parseInt(this.date.substring(0, yearIdx))
    },
    get month() {
        let yearIdx = this.date.indexOf("年")
        let monthIdx = this.date.indexOf("月")
        return parseInt(this.date.substring(yearIdx + 1, monthIdx))
    },
    get day() {
        let monthIdx = this.date.indexOf("月")
        let dayIdx = this.date.indexOf("日")
        return parseInt(this.date.substring(monthIdx + 1, dayIdx))
    },
}

wedding.date = "2020年10月20日"

alert(
    `那是${wedding.year}年的时候,${wedding.month}月的天气有些寒冷,${wedding.day}号的下午我和你妈妈举行了婚礼。`
) // 那是2020年的时候,10月的天气有些寒冷,20号的下午我和你妈妈举行了婚礼。

$$