iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 19
2
Modern Web

JavaScript基本功修煉系列 第 19

JavaScript基本功修練:Day19 - 設定物件屬性裏的特徵

  • 分享至 

  • xImage
  •  

在學習原型鏈的同時,自己也有看過關於物件屬性特徵的知識,才知道原來物件屬性裏還有特徵這個東西存在!我們可以定義這些物件的屬性特徵,也可以用一些方法操控或修改它們。

這篇文章會講解:

  • 定義物件屬性特徵
  • 設定屬性特徵的方法(preventExtensions、seal、freeze)
  • Get和Set

定義物件屬性特徵

物件屬性裏有幾個特徵:

  1. 值 (value)
  2. 可否被刪除 (configurable)
  3. 可否被寫入 (writable)
  4. 可否被列舉 (enumerable)

我們需要用Object.defineProperty來定義物件屬性特徵:

Object.defineProperty(物件, 屬性, 設定特徵)

範例如下:

const user = {
    a: 10,
    b: 20,
    c: 30
}

Object.defineProperty(user,'a',{
    value: 40,
    writable: false,
    configurable: true,
    enumerable: true
})
//value後面的3個特徵,預設都是true

//因為 writable: false ,所以以下的程式碼是不會有效
user.a = 100;

console.log(user.a) //40

這個例子是修改了屬性a的值以及writable特徵,我們重新賦值40,但是因為writablefalse,所以之後不能再重新賦予新的值。

user.a會變成一個靜默錯誤,JS執行它的時候並不會報錯。但在嚴謹模式下就會出錯:

(function(){
    'use strict';
    user.a = 100;
}())

之後我們看看configurableenumerable具體是指什麼。以下例子用defineProperties同時修改了多個屬性(b和c)的屬性特徵:

Object.defineProperties(user,{
    b: {
        configurable: false
    },
    c: {
        enumerable: false
    }
})

在進行下一步先,可以用Object.getOwnPropertyDescriptor來看看b和c是不是真的有被改掉特徵:

console.log(Object.getOwnPropertyDescriptor(user,'b'))
console.log(Object.getOwnPropertyDescriptor(user,'c'))

configurable是指可否被刪除,如果是false就不能被刪除,所以以下的程式碼是無效:

delete user.b 

enumerable是指可否被例舉,如果是false就不能被列舉,如果我們用for...in試試列舉屬性:

for( const key in user ){
    console.log(key) //a,b
}

淺層保護

要注意的是,這些屬性的設定只是針對整個物件,所以只是操控了物件裏最外層的屬性,它並不能操控最外層屬性裏的內層屬性,例如以下例子:

Object.defineProperty(user,'outerA',{
    value: {},
    writable: false,
    configurable: false
})

user.outerA = '123'
user.outerA.innerA = 'Hello'

console.log(user)

在以上例子中的outerA裏新增一個空物件。即使writablefalse,我們依然可以在那個空物件裏新增屬性innerA及值Hello,但就不能重新賦值予outerA

以上情況稱為淺層保護,我們不能操控巢狀屬性,只能操控最外層屬性。這裏修改屬性特徵的方式,只是針對物件本身。

設定屬性特徵的方法

除了採用上面提及的方式,逐一修改物件屬性特徵,我們還可以用一些方法,包括preventExtensionssealfreeze,去設定屬性特徵。

preventExtensions (禁止擴充):
無法新增屬性,可以重新配置特徵、調整目前屬性值(e.g: 修改屬性、新增巢狀屬性)

seal(封裝):
無法新增/刪除物件屬性,無法重新配置特徵,但可以調整目前屬性值(e.g: 修改屬性、新增巢狀屬性)

freeze(凍結):
無法新增/刪除物件屬性,無法重新配置特徵,無法調整目前屬性值(e.g: 修改屬性、新增巢狀屬性)

這裏我們拿其中一個,preventExtensions做驗證:

例如我們有以下的物件:

const user = {
    a: 10,
    b: 20,
    c: {}
}

preventExtensions的方法:

Object.preventExtensions(user)

//1. 修改屬性 (可以)
user.a = 100 

//2. 修改巢狀屬性 (可以)
user.c.inner = 'Hello'

//3. 新增屬性 (不可以)
user.d = 40;

//4. 刪除屬性 (可以)
delete user.a 

get與set方法

getset是物件的屬性方法,我們可以先定義一個屬性,並在該屬性裏使用這兩個方法,這些方法的角色有點像function一樣去處理不同的值。

get:取得特定值的方法
set:存值的方法

先看看set的用法:

const myScore = {
    total: 0,
    //把在set運算好的值,送回total
    set add(score){
        this.total = this.total + score
    }
}

myScore.add = 10;
console.log(myScore) //{total: 10}

set是存值的方法,當執行myScore.add時,它會把運算好的值,送回total裏,total會被重新賦值,所以說set有存值的功能。

那麼get呢?看看以下例子:

const myScore = {
    total: 0,
    //把在set運算好的值,送回total
    set add(score){
        this.total = this.total + score
    },
    get add(){
        return this.total + 100
    }
}

myScore.add = 10;
console.log(myScore.add) //110
console.log(myScore) //{total: 10}

get是取得特定值的方法,所以執行myScore.add,會得到經過get這個特定方法中的add運算後,得出來的值(110)。可是,當我們直接看myScore這個物件時,只會顯示total經過set方法的後,所更新的值(10),詳細看看console:

我們會見到add的值以(...)表示,當我們按下它後,就會顯示110,即是經過get方法運算出來的值:

如果我們在這裏查詢myScoremyScore.add

const myScore = {
    total: 0,
    //把在set運算好的值,送回total
    set add(score){
        this.total = this.total + score
    },
    get add(){
        return this.total + 100
    }
}
console.log(myScore.add,myScore) //100, {total: 0} (但(...)裏面是110)
myScore.add = 10;

第一個值會是100,因為目前total是0,透過add方法+100,變成100。
第二個值顯示{total: 0} (但(...)裏面是110),{total: 0}的原因是total初始值是0。而(...)裏面會是110,因為它把後面的myScore.add = 10都執行過,total就是 0 + 10 + 100,變成110。

總結

我們可以透過修改物件屬性的特徵,限制別人操控我們定義的屬性。同時,setget的方法也能讓我們像使用function一樣去處理在物件裏,或傳進物件裏的資料。

参考資料

JS核心篇(六角學院)
JAVASCRIPT.INFO
重新認識 JavaScript: Day 22 深入理解 JavaScript 物件屬性


上一篇
JavaScript基本功修練:Day18 - 用ES6的Class語法糖建立原型鏈
下一篇
JavaScript基本功修練:Day20 - this的運作
系列文
JavaScript基本功修煉31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言