當初在學習這一塊最困擾的地方就是為什麼我用Object.create()
建立一個新物件的時候,再console這個新物件居然是空的!後來才知道他只是用來繼承原本物件的prototype而已,property那些需要自己再定義。
不過也有看到別人這麼寫:
const obj = { name: "jade", age: 18 };
const newObj = Object.create(obj, { name: { value: "Ann" } });
console.log(newObj);
// {}
可是這麼做console出來也是一個空物件,可是我不是已經有給他value了嗎?
後來才學到原來屬性還有個屬性描述器可以去定義!
所以來整理一下我所學到了屬性描述器,明天會再介紹要怎麼去定義他們~
先來認識一下屬性(property)是什麼?一般來說屬性就是key, value的結合,但實際上這些屬性的背後還有可以設定的空間,我們把這些屬性設定叫做屬性描述器。
屬性描述器有六大類:
而這六大類其實裡面還有分兩小類:
這兩種描述器是不相容的。
因為資料描述器就是這個屬性的value(值)、writable(可否更改的);
而取值描述器get、 set是可以讓屬性去改變值,所以兩個不能同時存在。(後面有範例)
如果當configurable:false
表示其他描述器都不能被更改(但有個例外嘿)
const ann = {};
Object.defineProperties(ann, {
name: {
value: "ann",
writable: true,
enumerable: true,
configurable: false,
},
});
console.log(delete ann.name); // false
有一個特例是:就算configurable: false的狀態下,writable還是可以被更改成false。
表示就算不能動其他屬性描述器的情況下,也是被允許可以修改value的。
先設定name property的 configurable: false
的情況下,再重新設定一次把 writable: false
、 value: "hey"
都修改了,這樣不會報錯,value也的確被改到了。
const ann = {};
Object.defineProperty(ann, "name", {
writable: true,
enumerable: true,
configurable: false, => 已經設定不能修改
value: "ann"
});
// 再重新定義一次name:
Object.defineProperty(ann, "name", {
writable: false, => 偷偷修改
enumerable: true,
configurable: false,
value: "hey", => 偷偷修改
});
console.log(ann.name);
// hey
// 被改到了
但如果其他的一改變就會報錯,偷偷把enumberable改成false..就報錯拉!
Object.defineProperty(ann, "name", {
writable: true,
enumerable: false, => 修改
configurable: false,
value: "hey",
});
console.log(ann.name);
// TypeError: Cannot redefine property: name
這個後面會講到,常常在用定義屬性描述器時如果忘記去定義enumberable: true
的話,這個object就看起來是個空物件,但他其實還是有,只是沒有被列舉出來而已。
如果一開始name的屬性設定writable: false
之後再去更改value,就會被忽略。
const ann = {};
Object.defineProperty(ann, "name", {
value: "ann",
writable: false,
enumerable: true,
configurable: false,
});
ann.name = "hey";
console.log(ann);
// ann 一樣,沒有被改到
const a = {};
Object.defineProperty(a, "name", {
value: "ann",
writable: true,
enumerable: true,
configurable: false,
get() {
return this.name;
},
set(name) {
return this.name;
},
});
console.log(a);
// TypeError: Invalid property descriptor. Cannot both specify accessors and a value or writable attribute, #<Object>
如何寫入:
const jade = {
name: "Jade",
lastName: "Chen",
get fullName() {
return this.name + this.lastName;
},
set fullName(value) {
[this.name, this.lastName] = value.split(" ");
},
};
jade.fullName = "Ann Pang";
console.log(jade.lastName);
Object.defineProperty()
去定義const jade = {
name: "jade",
lastName: "Chen"
}
Object.defineProperty(a, "fullName", {
get() {
return this.name + this.lastName;
}
set(value) {
[this.name, this.lastName] = value.split(" ");
}
})
還可以加上條件判斷,如果新輸入的名字長度太短就不給改變
const jade = {
name: "jade",
get checkName() {
return this.name;
},
set checkName(value) {
if (value.length < 4) {
console.log("名字不能小於四個字母!");
return;
} else {
this.name = value;
}
},
};
jade.checkName = "Pete";
console.log(jade.name);
// 名字不能小於四個字母!
// jade
雖然介紹了上面這幾種方式,但要知道這些設定只能淺層設定而已,就是只會影響到自有的物件
讓我們來看看,故意將name的value設定成一個object,那用平常的做法就改變不到那個value的物件。
const ann = {};
const inner = { innerName: "hey" };
Object.defineProperty(ann, "name", {
value: inner,
writable: false,
enumerable: true,
configurable: false,
});
ann.name = "jade";
console.log(ann.name);
// { innerName: "hey" }
// 沒有被改到
可是如果我們再加一層進去更改那個value的話,嘿嘿就改到了!
ann.name.innerName = "jade";
console.log(ann.name);
// { innerName: 'jade' }
get fullName() {...}
的方式。Object.defineProperty()
的話就不用,因為前面會先寫好要定義哪個property今天先這樣拉~明天見!
參考資料:JavaScript - 屬性描述器 (1)
【新手大哉問】property、key、value、reference,到底是什麼啦?
属性的 getter 和 setter
JS對象屬性中get/set與getter/setter是什麼
JavaScript - 屬性描述器 (2)
008 深入瞭解JavaScript核心:函式、物件、原型鏈 (下)