[JS] setter & getter
TL;DR
介紹setter以及getter。
參考資料
相關連結
setter
語法
{set prop(val) {...}}
{set [expression](val) {...}} // ES6新增可以使用計算屬性名(computed property name)表達式,綁定到給定函式
需要注意,當我們使用setter的時候,並不是使用parameter去傳入參數,而是使用等號(=)賦值!
基礎範例
透過範例了解如何使用setter:
const language = {
set current(name) {
this.log.push(name);
},
log: [],
};
language.current = 'EN';
language.current = 'FA';
console.log(language.log); // ["EN", "FA"]
計算屬性名範例
const expr = "foo";
const obj = {
baz: "bar",
set [expr](v) {
this.baz = v;
},
};
console.log(obj.baz); // "bar"
obj.foo = "baz"; // 跑 setter
console.log(obj.baz); // "baz"
getter
語法
{get prop() { ... } }
{get [expression]() { ... } }
範例
直接透過範例,來了解如何使用Getter。
const obj = {
log: ['a', 'b', 'c'],
get latest() {
return this.log[this.log.length - 1];
},
};
console.log(obj.latest); // c
計算機屬性名範例
var expr = "foo";
var obj = {
get [expr]() {
return "bar";
},
};
console.log(obj.foo); // "bar"
綜合範 例
定義setter:
const myWallet={
deposit:100,
set save(price){
this.deposit = this.deposit + price / 2; // 每次只會存入一半
}
}
myWallet.save=50;
// 使用"等號"觸發setter,存入一半的金額(i.e,25元)
console.log(myWallet.deposit); // 125
定義getter
const myWallet={
deposit:100,
set save(price){
this.deposit = this.deposit + price / 2; // 每次只會存入一半
},
get halfDeposit(){
return this.deposit / 2; // 取出的時候只會取得一半
}
}
myWallet.save=50; // 使用"等號"觸發setter,存入一半的金額(i.e,25元)
console.log(myWallet,myWallet.halfDeposit);
// {deposit: 125} 62.5
上方範例首先先透過*setter(save)*將25存入deposit屬性內
接著印出myWallet物件,可以看到裡面的存款(deposit)已經將預設值100加上剛剛的25了,並且透過*getter(halfDeposit)*取出存款的一半出來,所以根據return的內容會是62.5
getter的(...)
如果今天將setter(save)執行順序往後調整如下
const myWallet={
deposit:100,
set save(price){
this.deposit = this.deposit + price / 2; // 每次只會存入一半
},
get halfDeposit(){
return this.deposit / 2; // 取出的時候只會取得一半
}
}
console.log(myWallet,myWallet.halfDeposit);
// {deposit: 100} 50
myWallet.save=50;
看起來很合理,目前myWallet物件內的存款金額是100,並且透過getter取出一半所以是50。
但是,如果我們將myWallet物件內容展開,可以發現裡面其實存在一個halfDeposit的屬性,內容是(...)
這個屬性是,當我們只要有使用到getter的時候就會自動加上的(setter不會有此行為)
執行結果圖示
我們可以點擊(...)
展開內容,以這個範例來說,會出現62.5。
執行結果圖示
也就是說,該getter屬性會在點選的當下才計算。
在點選getter屬性的時候因為已經將myWallet.save=50
執行完畢了,所以才會顯示62.5。
測試:使用setTimeout設定十秒後才透過setter存入金額,會不會影響點選getter屬性的值
結論:會。
const myWallet={
deposit:100,
set save(price){
this.deposit = this.deposit + price / 2;
},
get halfDeposit(){
return this.deposit / 2;
}
}
setTimeout(() => {
myWallet.save=1000;
console.log('10秒後存入1000元');
}, 10000);
console.log(myWallet) // 測試馬上打開
console.log(myWallet) // 測試十秒後打開
等到時間到才開啟的halfDeposit:(...)
會顯示為300(100+1000/2=600,透過getter取出時只取出一半,所以是300)
defineProperty
設定getter及setter
範例
我們也可以直接透過defineProperty
設定getter以及setter。
如果還不熟悉definePrpoerty
,可以參考前面幾篇的內容。
var foo = { a: 0 };
Object.defineProperty(foo, "bar", {
set: function (x) {
this.a = x / 2;
},
get: function() {
return this.a+1;
}
});
foo.bar = 10; // Runs the setter, which assigns 10 / 2 (5) to the 'a' property
console.log(foo.a); // 5
console.log(foo.bar); // 6
屬性特徵差異
我們透過defineProperty
定義getter以及setter,與直接在物件實字定義時就寫入set
,get
的方式,兩者之間最大的差異在於他們的enumerable
以及configurable
會不同。
透過以下範例來了解:
const foo={
deposit:10,
set saveDeposit(price){
this.deposit=this.deposit+price/2;
},
get getDeposit(){
return this.deposit / 2;
}
}
const bar={
list:[],
}
Object.defineProperty(bar,"friends",{
set:function(friend){
this.list.push(friend);
},
get:function(){
return this.list;
}
})
console.log(Object.getOwnPropertyDescriptor(foo,'getDeposit'));
// {set: undefined, enumerable: true, configurable: true, get: ƒ}
console.log(Object.getOwnPropertyDescriptor(foo,'saveDeposit'));
// {get: undefined, enumerable: true, configurable: true, set: ƒ}
console.log(Object.getOwnPropertyDescriptor(bar,'friends'));
// {enumerable: false, configurable: false, get: ƒ, set: ƒ}
可以發現直接在物件創建階段就定義好的getter及setter的enumerable
以及configurable
都會是true
而直接利用defineProperty
設定的getter及setter,其在enumerable
及configurable
特徵則都會是false
另外,也會發現getter和setter的屬性特徵物件並沒有value以及writable的屬性。
如果希望透過defineProperty
設定的getter及setter的屬性特徵與直接在物件初始化定義的特徵相同,我們可以調整如下:
const bar={
list:[],
}
Object.defineProperty(bar,"friends",{
confiturable:true,
enumerable:true,
set:function(friend){
this.list.push(friend);
},
get:function(){
return this.list;
}
})
console.log(Object.getOwnPropertyDescriptor(bar,'friends'));
// {enumerable: true, configurable: false, get: ƒ, set: ƒ}
delete
移除getter及setter
以以下範例 來說:
var foo = { a: 0 };
Object.defineProperty(foo, "bar", {
configurable:true, // 記得修改成true,才可以使用delete運算子刪除
set: function (x) {
this.a = x / 2;
},
get: function() {
return this.a+1;
}
});
console.log(foo); // 觀察目前的foo物件存在我們所定義的"bar" getter & setter (同名)
delete foo.bar // 使用delete運算子刪除,因為getter & setter 同名,所以兩個都會被移除
console.log(foo);
console.log(foo.bar);
foo.bar=100;
刪除前:
刪除後: