JS 屬性特徵
屬性特徵是甚麼
我們可以使用Object.defineProperty
調整屬性特徵,但defineProperty
不能對子屬性造成限制,屬於淺層保護
可以調整特徵為:
- 值
- 可否寫入
- 可否被刪除
- 可否被列舉
此種用法比較常在大型專案使用到,例如Vue
列舉以下範例說明,可調整物件屬性的特徵,如果writable
為false
,對此屬性進行賦值,將會失敗,會有靜默的錯誤,可以在嚴格模式下查看,如想一次定義大量屬性特徵,可以使用defineProperties
var example = { a: 1, b: 2, c: 3, };
Object.defineProperty(example, "a", { value: 4, writable: false, configurable: true, enumerable: true, });
example.a = 10;
delete example.a;
for (var key in example) { console.log(`列舉${key}`); }
(function () { "use strict"; example.a = 10; })();
Object.definePropreties(example, { a: { configurable: false, }, b: { writable: false, }, });
|
屬性延伸方法
防止擴充preventExtensions
Object.preventExtensions()
可用來避免物件被新增新的屬性,物件如果可以被增加新的屬性,我們稱它可以被擴充(extensible)
。
Object.preventExtensions()
標註物件使它無法被擴充,所以在它被標註為無法擴充當下,它將無法再增加新的屬性。
var example = { a: 1, b: 2, c: 3, };
Object.preventExtensions(example); console.log(`是否可被擴充${Object.isExtensible(example)}`);
Object.defineProperty(example, "new", { value: 8675309 });
|
封裝seal
MDN: Object.seal()
方法封閉一個對象,阻止添加新屬性並將所有現有屬性標記為不可配置。當前屬性的值只要原來是可寫的就可以改變。
簡單來說就是無法新增刪除擴充,也無法配置特性,但如果原屬性值可寫入就可調整該屬性值
var example = { a: 1, b: 2, c: 3, };
Object.seal(example); console.log(`是否可被擴充${Object.isExtensible(example)}`);
Object.defineProperty(example, "new", { value: 8675309 });
example.a = 111; console.log(example.a);
|
凍結freeze
MDN: Object.freeze()
方法可以凍結一個對象。一個被凍結的對象再也不能被修改;凍結了一個對象則不能向這個對象添加新的屬性,不能刪除已有屬性,不能修改該對像已有屬性的可枚舉性、可配置性、可寫性,以及不能修改已有屬性的值。此外,凍結一個對像後該對象的原型也不能被修改。 freeze()
返回和傳入的參數相同的對象。
簡單來說就是,物件加上seal
,並且無法更改值,原型也不能被修改
var example = { a: 1, b: 2, c: 3, };
Object.freeze(example); console.log(`是否可被擴充${Object.isExtensible(example)}`);
Object.defineProperty(example, "new", { value: 8675309 });
example.a = 111; console.log(example.a);
|
屬性列舉與原型關係
如果我們使用建構函式建立一個物件,使用for
將物件屬性輸出出來看的時候,會發現原型的屬性也一併輸出出來,
為什麼會如此可以查看他們原型的屬性,joe
的 name 屬性特徵,可以發現它是可以被列舉的,因此才會輸出
function Person() {} Person.prototype.name = "人類";
Object.defineProperty(Person.prototype, "name", { enumerable: false, });
var joe = new Person(); joe.a = 1;
for (var key in joe) { console.log(`key:${key}`); }
console.log(Object.getOwnPropertyDescriptor(joe.__proto__, "name")); console.log( Object.getOwnPropertyDescriptor(joe.__proto__.__proto__, "toString") );
for (var key in joe) { if (joe.hasOwnProperty(key)) { console.log(`key:${key}`); } }
|
Getter 和 Setter
set
語法會在物件屬性被嘗試定義時,將其屬性綁定到要呼叫的函式內。
get
語法會將物件屬性,綁定到屬性被檢索時,所呼叫的函式。
從以下範例可以看到可以透過set
跟get
賦值運算不使用函式
var wallet = { total: 100, set save(price) { this.total = this.total + price - 5; }, get save() { return this.total / 2; }, };
wallet.save = 500; console.log(wallet);
|
也可以使用Object.defineProperty
的方式賦予
var wallet = { total: 100, };
Object.defineProperty(wallet, "save", { set: function (price) { this.total = this.total + price - 5; }, get: function () { return this.total / 2; }, });
wallet.save = 500;
console.log(wallet); console.log(Object.getOwnPropertyDescriptor(wallet, "save"));
|
輸出如下可以看到屬性特徵被設為不可刪除及不可列舉,如有需要也可在defineProperty
將他更改為true
也可以在陣列原型上使用
var lists = [1, 2, 3];
Object.defineProperty(Array.prototype, "latest", { get: function () { return this[this.length - 1]; }, });
console.log(lists.latest);
|