iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 12
0
Modern Web

重新學習網頁設計系列 第 12

DAY 12. JavaScript 物件特性設置

DAY 12. JavaScript 物件特性設置

Getter and Setter

es6gettersetter使我們能夠更優雅的存取物件特性
參考以下範例:

const USER_EMAIL = Symbol()
class User {
    setEmail(value) {
        // do some validate here ..
        this[USER_EMAIL] = value
    }
    getEmail() {
        return this[USER_EMAIL]
    }
}

const Jerry = new User
Jerry.setEmail('test@mail.com')
Jerry.getEmail()    // "test@mail.com"

這樣的方式雖然也相當不錯,但如果能夠寫成以下這種方式,那就更好了 !

const Sam = new User
Sam.email = 'test2@mail.com'
console.log(Sam.email)   // "test2@mail.com"

其實相當簡單,我們只需要使用es6settergetter

const USER_EMAIL = Symbol()
class User {
    set email(value) {
        // do some validate here ..
        this[USER_EMAIL] = value
    }
    get email() {
        return this[USER_EMAIL]
    }
}

const Sam = new User
Sam.email = 'test2@mail.com'
console.log(Sam.email)   // "test2@mail.com"

現在我們有兩個函示來處理單一特性
並且js能夠很智能的在特性賦值使調用setter
或在特性別調用時使用getter
另外我們也可以只設定getter而不設定setter

物件特性屬性

在一般情況下我們正常的存取物件特性並不大礙
但其實特性本身也還有一起值得我們去了解的地方
那就是特性的屬性

  • 可寫性
    就如字面所說,控制著該特性是否可以變更修改
  • 可枚舉性
    該特性是否能夠被for/inObject.keys擴張運算子枚舉
  • 可設定性
    控制著特性是否能被刪除或是修改特性屬性

控制特性屬性必須使用Object.defineProperty來操作
我們可以用它來建立新的物件特性,或是修改既有的特性

例如我們想讓物件obj的foo特性唯獨,可以這樣子做

const obj = { foo: 'bar' }
Object.defineProperty(obj, 'foo', { writable: false })

// 現在如果我們嘗試修改特性foo的值,結果將不會成功
obj.foo = 100
console.log(obj)    // { foo: 'bar' }

當我們有一個既有的物件,我們也可以使用屬性設定的方式新增gettersetter特性

const obj = {}
const USER_EMAIL = Symbol()
Object.defineProperty(obj, 'email', {
    set(value) {
        // do some validate here ...
        this[USER_EMAIL] = value
    },
    get() {
        return this[USER_EMAIL]
    }
})

obj.email = 'test@mail.com'
console.log(obj.email)  // "test@mail.com"

有時候我們會設定非基值特性給物件,通常它們不該能夠被枚舉出來
為了達到此目的我們可以使用特性屬性enumerable

const numbers = [15, 9, 37, 98]
numbers.total = function () { return this.reduce((a, x) => a + x, 0) }
numbers.avg = function () { return this.total() / this.length }
Object.defineProperty(numbers, 'total', {enumerable: false})
Object.defineProperty(avg, 'total', {enumerable: false})
console.log(numbers.total())    // 159
console.log(numbers.avg())      // 39.75

個人疑問
在書中讀到這個部分後實際操作
發現就算不使用Object.defineProperty
totalavg一樣不會被納入計算,希望有前輩能夠給予指點。

最後綜合以上所學, 我們可以這樣把玩範例

const USER_EMAIL = Symbol()
const user = { name: 'Peter' }

// 將預設屬性 name 設為不可枚舉
Object.defineProperty(user, 'name', { enumerable: false })

// 使用setter, getter
Object.defineProperty(user, 'email', {
    set(value) {
        // do some validate here ...
        this[USER_EMAIL] = value
    },
    get() {
        return this[USER_EMAIL]
    }
})

// 嘗試定義一個回傳不可枚舉特性的方法
Object.defineProperty(user, 'greet', {
    value() { return `Hello my name is ${this.name}` }
})

console.log(user)            // {}
user.mail = 'test@mail.com'
console.log(user)           // { mail: 'test@mail.com' }
console.log(user.greet())   // Hello my name is Peter

for (let prop in user) {
    console.log(prop)
}
// mail

或許我們很少使用Object.defineProperty
但使用特性屬性來保護物件特性能使程式更加健壯
尤其是我們在開發一些很可能被他人使用的物件時
我們無法保證使用者能夠很安全的枚舉或修改必要物件特性
此時Object.defineProperty才真正地發揮了它的特長


上一篇
DAY 11. JavaScript Map and Set
下一篇
DAY 13. JavaScript URL API
系列文
重新學習網頁設計30

尚未有邦友留言

立即登入留言