Day23 | 物件方法介紹 - 屬性特徵列舉與原型的關係、Setter 與 Getter 運用
屬性列舉與原型的關係
我們所制定的「 原型 」與「 原生原型 」最大不同就是在屬性特徵可列舉的部分 enumerable
,因可列舉的關係 for in
方式不會出現原生原型的內容但會出現我們制定原型的內容。
範例 1. 自制的原型屬性與原生原型屬性關係
1 | // 原型概念須先產生建構函式 |
「 自制的原型屬性 」與 「 原生原型屬性 」的屬性特徵 enumerable
不同,使用此範例來拆解不同處。
|拆解|
➤ 於建構函式新增原型內容 → 透過建構函式產生新物件 & 賦予新屬性
- prototype 為原型中的屬性,casper 內容可見 prototype 中的 name 屬性顏色和其他 prototype 屬性顏色不同,和我們自己定義的屬性色彩
a
相同。
➤ 變數 casper
使用 hasOwnProperty
分別查看 a
與 name
屬性:
1 | function Person() {} |
- 雖然原型鏈的概念會不斷向上尋找,但
hasOwnProperty
是針對物件當前的屬性來來查看。 - casper 變數使用
for in
方式列舉出所有屬性包含使用建構函式新增的內容name
但hasOwnProperty
無法呈現出prototype
中的name
是因為hasOwnProperty
只針對當前屬性來查看。
➤ 使用 for in
方式列出 casper 內的屬性
1 | function Person() {} |
for in
方式列出 casper 內的屬性有a
與自訂原型中的name
。- 因為可列舉的關係 for in 方式不會出現原生原型的內容但會出現我們制定原型的內容
name
。
- 因為可列舉的關係 for in 方式不會出現原生原型的內容但會出現我們制定原型的內容
➤ 我們自制原型的屬性與原生原型屬性的「 屬性特徵不同 」
回到 Day21 | 物件的方法介紹-針對物件本身屬性操作的方法 中的
definedProperty
,它可針對屬性額外去定義屬性特徵 (value
、writable
、configurable
、enumerable
)範例中我們自制的原型屬性
name
與原型中的屬性特徵不同,差異在於:使用
Object.getOwnPropertyDescriptor(物件)
去得物件裡屬性的特定特,因為要查詢 casper 原型中的 name,所以結構如下![https://ithelp.ithome.com.tw/upload/images/20221008/20119743aIIWfQHhPQ.png](https://ithelp.ithome.com.tw/upload/images/20221008/20119743aIIWfQHhPQ.png)1
2
3
4
5
6function Person() {}
Person.prototype.name = '人類';
var casper = new Person();
casper.a = undefined;
console.log(Object.getOwnPropertyDescriptor(casper.__proto__, 'name'));再 casper 進到物件原型中的物件原型中的其中一個方法 toString。
![https://ithelp.ithome.com.tw/upload/images/20221008/20119743v1yFuwbyEK.png](https://ithelp.ithome.com.tw/upload/images/20221008/20119743v1yFuwbyEK.png)1
2
3
4
5
6function Person() {}
Person.prototype.name = '人類';
var casper = new Person();
casper.a = undefined;
console.log(Object.getOwnPropertyDescriptor(casper.__proto__, 'name'));
由上方查詢屬性特徵看到我們制定的原型與原生原型的
enumerable
列舉方式不同。- 我們制定的原型屬性特徵
enumerable: true
( 可列舉 ),原生原型屬性特徵enumerable: false
( 不可列舉 )。 - 就因可列舉的特徵,所以我們制定的原型屬性
name
在for in
方式下是會被列出來的。
- 我們制定的原型屬性特徵
➤ 我們制定的原型與原生的原型最大的不同在於我們制定的原型是可被列舉,原生原型是不可被列舉
❒ 解決自制的原型屬性與原生原型屬性屬性特徵不同的方式
解決方式
使用 definedProperty
對物件屬性做調整。關於 definedProperty
可參考前面的筆記 Day21 | 物件的方法介紹-針對物件本身屬性操作的方法 。
1 | // 承上範例 1. |
自制原型屬性 name 使用
definedProperty
把屬性特徵enumerable
調整為 false ( 不可被列舉 ) 與原生原型一樣 ,console.log(casper);
可見name
顏色就與其他原生原型一樣了。- 另外
Object.definedProperty(物件, '屬性', 參數);
中物件放入 Person 是因為函式也是物件的一種。
- 另外
Object.getOwnPropertyDescriptor(casper.__proto__, 'name')
中enumerable
屬性特徵也與原生原型一樣是false
。
使用
for in
方式就看不到name
屬性。
解決使用 for in 列舉的問題
使用 for in
可以一一列出物件屬性們,但 for in
不會分辨此屬性是自制原型或原生原型,由於自制原型預設是可被列舉,所以就會被列出來。
➤ 沒使用 definedProperty
調整 enumerable
屬性特徵遇到的問題
如果沒有使用 definedProperty
調整 enumerable
屬性特徵,使用 for in
就會一一列出物件屬性們,包含自制原型內的屬性。 CodePen 範例
// casper 物件下的內容 ↑
➤ 解決使用 for in 不列出原型屬性的方式
- 使用
definedProperty
調整enumerable
。CodePen 範例 - 在
for in
加上判斷式。CodePen 範例- 在
for in
內加上一段判斷式來確保此屬性是在當前物件下而非原型內。for in
中的key
就只會顯示當前物件的屬性a
。
- 在
1 | function Person() {} |
❒ Getter 與 Setter,賦值運算不使用函式
想變更物件屬性值,但又希望他有運算功能,可使用 Getter
( 取得特定值的方法 ) 與 Setter
( 存值的方法 ) 。
範例1. Setter 存值運用
1 | var wallet = { |
Setter 使用方式
Setter
是存值概念,會傳入參數
- 先使用一個
set
關鍵字,後方加上屬性名稱 ( 為函式save(參數){}
),這樣在 Setter 裡面就可以透過參數方式把值傳入。會透過函式內的參數且透過運算來改變total
這個屬性值。- 一般都會直接使用
wallet.total = 新值;
來改變total
屬性值,但 Setter 是透過運算方式改變。
- 一般都會直接使用
- 如何透過 Setter 把值存入?
- 可使用
物件.屬性名稱 = 新值;
(wallet.save = 300;
),等號右邊的值會透過參數的方式傳進去。 - ❗ 注意:這邊使用等號
=
非()
,所以並非用函式方式來執行Setter
,是用等號賦予值的方式來改變存值的方式。
- 可使用
答案
- 答案為
250
。
範例 2. Getter 取值運用
1 | var wallet = { |
使用方式
Getter
是取值的概念,不會傳入參數,因為是取值所以會用到 return
。
答案
由開發者工具可見有一個
save: (…)
,Getter
的值是在按下(…)
出現的值。
驗證:
Getter
的值是在按下(…)
出現的值,與wallet.save
相同為 50。
範例 3. Setter 存值和 Getter 取值一起使用
1 | var wallet = { |
使用方式
set
與get
屬性名稱命名可一樣也可不一樣。- 如果
set
與get
兩個一起運用,要注意Getter
值是在點下(…)
出現的值。
答案
1 | set 部分 100+(300/2) = 250 |
Setter
透過參數方式把值 300 存入經過運算,total
為250
。Getter
取到的值為 125。
❒ Getter 與 Setter 搭配 Object.defineProperty
defineProperty 架構
Object.defineProperty(物件, ‘屬性名稱’, 參數);
範例 1. 延續上方範例,使用 defineProperty 方式
1 | var wallet = { |
屬性名稱帶上
'save'
,參數裡面使用set
冒號function
方式把 Setter 加入。使用defineProperty
的方式在開發者工具中看見 Getter 值為不同顏色,屬性特徵與 「 Getter 與 Setter,賦值運算不使用函式 」 新增方式不同 。
// console.log(wallet);
使用
Object.getOwnPropertyDescriptor
查看物件的'save'
屬性特徵。configurable
與enumerable
皆為false
,顯示不可刪除與不可列舉。
調整屬性特徵 configurable
與 enumerable
1 | var wallet = { |
- 在
defineProperty
參數中加入configurable
與enumerable
即可。wallet
物件中的save
變深色 &屬性特徵configurable
與enumerable
也調整為true
( 可刪除也可列舉 )。
❒ 加碼範例 - 陣列使用 Getter 與 Setter
取出陣列中最後一筆資料
1 | var a = [1, 2, 3]; |
- 使用
defineProperty
直接操作陣列的原型Array.prototype
( 可透過陣列的建構函式來調整它 ),並加上自定義屬性名稱與帶入參數 Getter。 - Getter 中
return
的this
為陣列本身。 - 因為直接修改陣列原型,所以所有陣列都可以使用此方法,使用
console.log(a.last);
取最後一個值。
參考資訊
- JavaScript 核心篇