Object.defineProperty && Object.defineProperties
ECMAS-262第5版在定義只有內部采用的特性時,提供了描述了屬性特征的幾種屬性。ECMAScript對象中目前存在的屬性描述符主要有兩種,數據描述符(數據屬性)和存取描述符(訪問器屬性),數據描述符是一個擁有可寫或不可寫值的屬性。存取描述符是由一對 getter-setter 函數功能來描述的屬性。
Object.defineProperty && Object.defineProperties
這兩個方法在js中十分重要,主要功能就是用來定義或修改內部屬性,與之相對應的Object.getOwnPropertyDescriptor && Object.getOwnPropertyDescriptors
就是獲取這行內部屬性的描述。
數據(數據描述符)屬性
數據屬性有4個描述內部屬性的特性
-
[[Configurable]]
表示能否通過delete刪除此屬性,能否修改屬性的特性,或能否修改把屬性修改為訪問器屬性,如果直接使用字面量定義對象,默認值為true -
[[Enumerable]]
表示該屬性是否可枚舉,即是否通過for-in循環或Object.keys()返回屬性,如果直接使用字面量定義對象,默認值為true -
[[Writable]]
能否修改屬性的值,如果直接使用字面量定義對象,默認值為true -
[[Value]]
該屬性對應的值,默認為undefined
訪問器(存取描述符)屬性
訪問器屬性也有4個描述內部屬性的特性
-
[[Configurable]]
和數據屬性的[[Configurable]]一樣,表示能否通過delete刪除此屬性,能否修改屬性的特性,或能否修改把屬性修改為訪問器屬性,如果直接使用字面量定義對象,默認值為true -
[[Enumerable]]
和數據屬性的[[Configurable]]一樣,表示該屬性是否可枚舉,即是否通過for-in循環或Object.keys()返回屬性,如果直接使用字面量定義對象,默認值為true -
[[Get]]
一個給屬性提供 getter 的方法(訪問對象屬性時調用的函數,返回值就是當前屬性的值),如果沒有 getter 則為 undefined。該方法返回值被用作屬性值。默認為 undefined -
[[Set]]
一個給屬性提供 setter 的方法(給對象屬性設置值時調用的函數),如果沒有 setter 則為 undefined。該方法將接受唯一參數,並將該參數的新值分配給該屬性。默認為 undefined
創建/修改/獲取屬性的方法
- Object.defineProperty()
功能:
方法會直接在一個對象上定義一個新屬性,或者修改一個對象的現有屬性, 並返回這個對象。如果不指定configurable, writable, enumerable ,則這些屬性默認值為false,如果不指定value, get, set,則這些屬性默認值為undefined
語法: Object.defineProperty(obj, prop, descriptor)
obj: 需要被操作的目標對象
prop: 目標對象需要定義或修改的屬性的名稱
descriptor: 將被定義或修改的屬性的描述符
var obj = new Object();
Object.defineProperty(obj, 'name', {
configurable: false,
writable: true,
enumerable: true,
value: '張三'
})
- Object.defineProperties()
功能:
方法直接在一個對象上定義一個或多個新的屬性或修改現有屬性,並返回該對象。
語法: Object.defineProperties(obj, props)
obj: 將要被添加屬性或修改屬性的對象
props: 該對象的一個或多個鍵值對定義了將要為對象添加或修改的屬性的具體配置
var obj = new Object();
Object.defineProperties(obj, {
name: {
value: '張三',
configurable: false,
writable: true,
enumerable: true
},
age: {
value: 18,
configurable: true
}
})
console.log(obj.name, obj.age) // 張三, 18
- Object.getOwnPropertyDescriptor()
功能:
該方法返回指定對象上一個自有屬性對應的屬性描述符。(自有屬性指的是直接賦予該對象的屬性,不需要從原型鏈上進行查找的屬性)
語法: Object.getOwnPropertyDescriptor(obj, prop)
obj: 需要查找的目標對象
prop: 目標對象內屬性名稱
var person = {
name: '張三',
age: 18
}
var desc = Object.getOwnPropertyDescriptor(person, 'name');
console.log(desc) 結果如下
// {
// configurable: true,
// enumerable: true,
// writable: true,
// value: "張三"
// }
- Object.getOwnPropertyDescriptors()
功能:
所指定對象的所有自身屬性的描述符,如果沒有任何自身屬性,則返回空對象。
語法: Object.getOwnPropertyDescriptors(obj)
obj: 需要查找的目標對象
var person = {
name: '張三',
age: 18
}
var desc = Object.getOwnPropertyDescriptors(person);
console.log(desc)
//name: {
// configurable: true,
// enumerable: true,
// value: '張三',
// writable: true
//}
//age: {
configurable: true
enumerable: true
value: 18
writable: true
}
各種場景下描述符屬性
- configurable
如果設置configurable屬性為false,則不可使用delete操作符(在嚴格模式下拋出錯誤), 修改所有內部屬性值會拋出錯誤,在《javaScript高級教程中》說只可以改變writable的值,現在改變writable的值也會拋出錯誤
在對象中添加一個數據描述符屬性
var person = {};
Object.defineProperty(person, 'name', {
configurable: false,
value: 'John'
}) ;
delete person.name // 嚴格模式下拋出錯誤
console.log(person.name) // 'John' 沒有刪除
Object.defineProperty(person, 'name', {
configurable: true //報錯
});
Object.defineProperty(person, 'name', {
enumerable: 2 //報錯
});
Object.defineProperty(person, 'name', {
writable: true //報錯
});
Object.defineProperty(person, 'name', {
value: 2 //報錯
});
在對象中添加存取描述符屬性
var obj = {};
var aValue; //如果不初始化變量, 不給下面的a屬性設置值,直接讀取會報錯aValue is not defined
var b;
Object.defineProperty(obj, 'a', {
configurable : true,
enumerable : true,
get: function() {
return aValue
},
set: function(newValue) {
aValue = newValue;
b = newValue + 1
}
})
console.log(b) // undefined
console.log(obj.a) // undefined, 當讀取屬性值時,調用get方法,返回undefined
obj.a = 2; // 當設置屬性值時,調用set方法,aValue為2
console.log(obj.a) // 2 讀取屬性值,調用get方法,此時aValue為2
console.log(b) // 3 再給obj.a賦值時,執行set方法,b的值被修改為2,額外說一句,vue中的計算屬性就是利用setter來實現的
注意:
1.getter和setter可以不同時使用,但在嚴格模式下只其中一個,會拋出錯誤
2.數據描述符與存取描述符不可混用,會拋出錯誤
var obj = {};
Object.defineProperty(obj, 'a', {
value: 'a1',
get: function() {
return 'a2'
}
});
3.全局環境下
var a = 1; // a屬於window, 相當於window.a
var d = Object.getOwnPropertyDescriptor(window, 'a');
console.log(d)
// {
// configurable: false,
// value: 1,
// writable: true,
// enumerable: true
// }
在來看一下另一種不適用var聲明的方式初始化a變量
a = 1; // a相當於window的一個屬性, window.a
var d = Object.getOwnPropertyDescriptor(window, 'a');
console.log(d)
// {
// configurable: true, // 此時configurable屬性值為true
// value: 1,
// writable: true,
// enumerable: true
// }
使用 var定義的任何變量,其configurable屬性值都為false,定義對象也是一樣
var b = {
name: 'bbb'
}
var d = Object.getOwnPropertyDescriptor(window, 'b');
console.log(d)
// {
// configurable: false
// value: 1,
// writable: true,
// enumerable: true
// }
但是這里需要說明的一點是,使用字面量定義的對象,該對象內部的屬性的數據描述符屬性都為true
var b = {
name: 'bbb'
}
var d = Object.getOwnPropertyDescriptor(b, 'name');
console.log(d)
// {
// configurable: true
// writable: true,
// enumerable: true
// value: 'bbb'
// }
- Writable
當writable為false(並且configurable為true),[[value]]可以通過defineProperty修改, 但不能直接賦值修改
//-----------------------------------通過defineProperty修改--------------------------------
var obj = {};
Object.defineProperty(obj, 'a', {
configurable: true,
enumerable: false,
writable: false,
value: 1
});
Object.defineProperty(obj, 'a', {
configurable: false,
enumerable: true,
writable: false ,
value: 2
});
var d = Object.getOwnPropertyDescriptor(obj, 'a')
console.log(d); // 結果如下
// {
// value: 2,
// writable: false,
// enumerable: true,
// configurable: false
// }
//-------------------------------------------直接賦值修改-----------------------------------
var obj = {}
Object.defineProperty(obj, 'a', {
configurable: true,
enumerable: false,
writable: false,
value: 1
});
obj.a=2;
var d = Object.getOwnPropertyDescriptor(obj, 'a')
console.log(d); // 結果如下
// {
// value: 1, // 沒有做出修改
// writable: false,
// enumerable: true,
// configurable: false
// }
- Enumerable
var obj = {};
Object.defineProperties(obj, {
a: {
value: 1,
enumerable: false
},
b: {
value: 2,
enumerable: true
},
c: {
value: 3,
enumerable: false
}
})
obj.d = 4;
//等同於
//Object.defineProperty(obj, 'd', {
// configurable: true,
// enumerable: true,
// writable: true,
// value: 4
//})
for(var key in obj) {
console.log(key);
// 打印一次b, 一次d, a和c屬性enumerable為false,不可被枚舉
}
var arr = Object.keys(obj);
console.log(arr); // ['b', 'd']