一、定义属性
使用 Object 构造函数或者使用对象的字面形式:
var person1 = {
name: "Tom"
};
var person2 = new Object();
person2.name = "Jerry";
对象的属性可以任意修改,当一个属性第一次被添加给对象时,JS 在对象上调用一个名为 [[Put]] 的内部方法,该方法会在对象上创建一个新的节点来保存属性。
调用 [[Put]] 的结果是在对象上创建了一个自有属性。一个自有属性表明仅仅该指定的对象实例拥有该属性。该属性被直接保存在实例内。
当一个已有的属性被赋予新的值时,调用的是一个名为 [[Set]] 的方法。
二、属性探测
1. in 操作符
in 操作符会检查自有属性和原型属性。
console.log("name" in person1); // true
2. hasOwnProperty() 方法
hasOwnProperty() 方法在给定的属性存在且为自由属性时返回true。
console.log(person1.hasOwnProperty("name")); // true
console.log("toString" in person1); // true
console.log(person1.hasOwnProperty("toString")); // false
三、删除属性—— delete 操作符
将属性值设置为 null 并不能彻底删除该属性。
delete person1.name;
console.log("name" in person1); // false
四、属性枚举
所有添加的属性默认都是可枚举的,也就是说可以用 for-in 来进行遍历。可枚举属性的内部特征 [[Enumerable]] 都被设置为 true。
Object.keys() 方法可获取对象可枚举的属性列表。
区别:for-in 循环同时也会遍历原型属性,而 Object.keys() 只返回自由属性。
并不是所有属性都是可枚举的,大多数原生方法的 [[Enumerable]] 属性都被设置为 false,可以通过 propertyIsEnumerable() 方法来检查一个属性是否可枚举。
var array = [1,2];
console.log("length" in array); // true
console.log(array.propertyIsEnumerable("length"); // false
五、属性类型
1. 数据属性
数据属性包含一个值,比如 person1 中的 name 属性。
[[Put]] 方法的默认行为是创建数据属性。
2. 访问器属性
访问器属性不包含值,而是定义了一个当属性被读取时被调用的函数(getter),和一个当属性被写入时调用的函数(setter)。访问器属性仅需要两者中的任意一个,也可以两者都有。
var person = {
_name: 'Tom',
get name() {
// do something
return this._name;
},
set name(value) {
// do something
this._name = value;
}
}
如果仅定义 getter ,该属性就变成只读,在非严格模式下试图写入将失败,而在严格模式下将抛错;如果仅定义 setter ,该属性就变成只写,两种模式下试图读取都将失败。
六、属性特征
1. 通用特征
数据属性和访问器属性都具有的特征:
- [[Enumerable]]:可枚举,决定是否可以遍历该属性;
- [[Configurable]]:该属性是否可配置;
如果想改变属性特征,可以使用 Object.defineProperty() 方法。该方法接收三个参数:拥有该属性的对象、属性名、包含需要设置特征的属性描述对象。
Object.defineProperty(person1, "name", {
enumerable: false, //不可枚举,propertyIsEnumerable() 方法将返回 false
configurable: false //不可配置,该属性将不可改变,试图删除该属性将会失败(严格模式下回抛错),且再次调用 Object.defineProperty() 也不会改变属性
});
当用 Object.defineProperty() 定义新的属性时一定要记得为所有的特征指定一个值,否则布尔型的特征会被默认设置为 false 。
2. 数据属性特征
- [[Value]]:包含属性的值。在对象上创建属性时该特征被自动赋值。所有的属性的值都保存在 [[Value]] 中,哪怕该值是个函数。
- [[Writable]]:该属性是否可以写入。
3. 访问器属性特征
- [[Get]]:内含 getter 函数;
- [[Set]]:内含 setter 函数
使用访问器属性特征比使用对象字面形式定义访问器属性的优势在于,你可以为已有的对象定义这些属性;而字面形式只能在创建对象时定义访问器属性。
var person1 = {
_name: "Tom"
};
Object.defineProperty(person1, "name", {
get: function() {
return this._name;
},
set: function(value) {
this._name = value;
},
enumerable: true,
configurable: true
})
如果你试图创建一个同时具有数据特征和访问器特征的属性,将会抛错
4. 定义多重属性
Object.defineProperties(): 为一个对象定义多个属性,接收两个参数:需要改变的对象、一个包含所有属性信息的对象。后者可以被看成一个哈希表,key 是属性名,value 是为该属性定义特征的属性描述对象。
var person = {};
Object.defineProperties(person, {
_name: {
value: "Tom",
enumerable: true,
configurable: true,
writable: true
},
name: {
get: function() {
return this._name;
},
set: function(value) {
this._name = value;
},
enumerable: true,
configurable: true
}
})
5. 获取属性特征—— Object.getOwnPropertyDescriptor(object, property)
这个方法只适用于自有属性,接收两个参数:对象、属性名。如果属性存在,会返回一个属性描述对象,内含4个属性:configurable、enumerable,另外两个根据属性类型决定。
七、禁止修改对象
对象和属性一样有指导其行为的内部特征,其中 [[Extensible]] 是一个布尔值,指明该对象本身是否可以被修改。
1. 禁止扩展—— Object.preventExtensions(object)
创建一个不可扩展的对象,该方法接收一个参数:即你希望不被扩展的对象。
一旦对一个对象使用该方法,就永远不能为该对象添加新的属性了。
可以用 Object.isExtensible() 来检查 [[Extensible]] 的值。
2. 对象封印—— Object.seal(object)
一个被封印的对象不可扩展( [[Extensible]] 为 false),且所有属性都不可配置( [[Configurable]] 为 false)。
即对象不可添加新属性、不可删除属性或改变其类型(从数据属性变成访问器属性或者相反),只能读写它的属性。
可以用 Object.isSealed() 来判断一个对象是否被封印。
3. 对象冻结—— Object.freeze(object)
对象不可添加新属性、不可删除属性或改变其类型、也不能写入任何数据属性,即被冻结对象是一个数据属性都为只读的被封印对象。
可以用 Object.isFrozen() 来判断对象是否被冻结。
本文内容摘自《面向对象精要》一书