第一個方法: for...in
先來看一段關於 MDN對於 for...in
的解釋:
for...in 迴圈只迭代可列舉屬性。....迴圈將迭代全部可列舉屬性,包括了物件自身的和物件繼承自它的建構式之原型的可列舉屬性。(原型鏈上較接近物件的屬性覆蓋原型的屬性)
意思是使用 for...in
不只將物件自己的可列舉特性取出,也會將繼承而來的可列舉特性也一併取出
來看看個測試例子:
var obj = {
sayHi:() => console.log('Hello!')
}
var obj2 = Object.create(obj);
obj2.name = "Bill";
obj2.habbit = "Read Books";
for( key in obj2){
console.log(key);
}
透過 Object.create()
讓 obj
傳入obj2
當作 obj2
的原型物件,此時使用 for...in
可以發現取出來的特性包含了繼承而來的特性
第二個方法: Obejct.keys()
MDN: Object.keys() 方法會回傳一個由指定物件所有可列舉之屬性組成的陣列,該陣列中的的排列順序與使用 for...in 進行迭代的順序相同(兩者的差異在於 for-in 迴圈還會迭代出物件自其原型鏈所繼承來的可列舉屬性)。
在 MDN 的解釋中,可以歸納出幾個重點:
Object.keys()
會得到指定物件的所有可列舉特性所組成的陣列。for...in
不同,並不會從原型物件中繼承來的可列舉特性也一併取出。將上一個測試例子改寫一下:
var obj = {
sayHi:() => console.log('Hello!')
}
var obj2 = Object.create(obj);
obj2.name = "Bill";
obj2.habbit = "Read Books";
const keys = Object.keys(obj2);
console.log(keys);
第三個方法: Object.getOwnPropertyNames()
透過 Object.getOwnPropertyNames()
,能回傳所有可列舉、不可列舉的特性所組成的陣列。
來個例子測試一下:
var obj = {
name: 'Bill',
habbit: 'Read books'
}
Object.defineProperty(obj,'weight',{
value: '65kg',
enumerable:false
})
var prop = Object.getOwnPropertyNames(obj);
console.log(prop);
將上述所提到取出列舉特性的部分做個表格歸納一下:
語法 | 可取得列舉特性 | 可取得原型物件列舉特性 | 可取得不可列舉特性 |
---|---|---|---|
for...in |
O | O | X |
Object.keys |
O | X | X |
Object.getOwnPropertyNames |
O | X | O |
關於怎麼判斷某物件是否可以擴充,或者如何設定某物件的擴充性,以下有一些方法可以達成。
第一個是: Object.isExtensible()
用來判斷某個物件是否具有擴充性
var obj = {
name: "Bill",
habiit: "Read books"
}
console.log(`obj物件是否具擴充性: ${Object.isExtensible(obj)}`);
接下來來看看不可擴充的情況
第二個是: Object.preventExtensions()
讓物件的特性不能再被新增到物件中。
但需要注意的是,物件依然可以刪除原本已存在的特性
寫個測試的例子驗證一下:
var obj = {
name: "Bill",
habbit: "Read books"
}
Object.preventExtensions(obj);
console.log(`obj物件是否具擴充性: ${Object.isExtensible(obj)}`);
obj.sayHi = () => console.log('Hello!');
delete obj.name;
var prop = Object.keys(obj);
console.log(`obj物件的特性有: ${prop}`);
為了檢查擴充性,所以新增了一個 sayHi
方法給 obj
物件,而透過結果可以發現,這個新增的特性並沒有被新增到 obj
中。
再來透過delete
刪除了 obj
物件的 name
特性,所以也就只剩下特性 habbit
了。
第三個是: Object.seal()
Object.seal()
有一些特性需要注意,如下:
Object.isSealed()
判斷該物件是否密封來寫個測試例子:
var obj = {
name: "Bill",
habbit: "Read books"
}
Object.seal(obj);
console.log('obj物件是否已經被密封(sealed): ' + Object.isSealed(obj));
// 查看 sayHi 方法能否被新增
obj.sayHi = () => console.log('Hello!');
console.log(obj);
// 查看 name 特性是否有被刪除
delete obj.name;
console.log(obj);
obj.name = "Jack";
// 查看 name 的特性是否被修改
console.log(obj);
第四個是: Object.freeze()
Object.freeze()
有一些特性需要注意,如下:
Object.isFrozen()
判斷該物件是否凍結來寫個測試例子:
var obj = {
name: "Bill",
habbit: "Read books"
}
Object.freeze(obj);
console.log('obj物件是否已經被凍結: ' + Object.isFrozen(obj));
// 查看 sayHi 方法能否被新增
obj.sayHi = () => console.log('Hello!');
console.log(obj);
// 查看 name 特性是否有被刪除
delete obj.name;
console.log(obj);
obj.name = "Jack";
// 查看 name 的特性是否被修改
console.log(obj);
當兩者搭配運用時,就會將其定義成 「存取器描述器」(accseeor descriptor)。
直接透過測試例子來看看怎麼運作:
先看看取值器(getter)的使用:
var obj = {
get sayHi() {
return 'Hello! ';
}
}
const respond = obj.sayHi;
console.log(respond);
取值器(getter) 會呼叫一個隱藏的函式(也就是 sayHi
),並回傳 Hello!
這個值給 respond
變數。
再看看設值器(setter)的使用:
var obj = {
name:this.name,
set getName(name) {
this.name = name;
}
}
obj.getName = "Bill";
console.log(obj.name);
getName
),並將 Bill
這個值做為 getName
的參數傳入,此時 obj
物件 name
的特性值已變成 Bill
name
特性的值時,就可以拿到 Bill
。最後看看兩者搭配的使用:
var obj = {
name:this.name,
get sayHi() {
return 'Hello! ' + this.name;
},
set getName(name) {
this.name = name;
}
}
obj.getName = "Bill";
const respond = obj.sayHi;
console.log(respond);
getName
),並將 Bill
這個值做為 getName
的參數傳入,此時 obj
物件 name
的特性值已變成 Bill
sayHi
),並回傳 Hello! Bill
這個值給 respond
變數。今天就先到這裡囉
明天見~