前言
理解JavaScript的对象运作方式对以后理解整个JS很有帮助,下面开始介绍。
定义属性
当一个属性第一次被添加给对象时,JavaScript在对象上调用一个名为[[Put]]
的内部方法。[[Put]]
方法会在对象上创建一个新节点来保存属性。调用[[Put]]
的结果是在对象上创建了一个自有属性
当一个已有的属性被赋予一个新值时,调用的是一个名为[[Set]]
的方法
属性探测
检查对象是否已有一个属性,最可靠的方法是使用 in
操作符:'属性' + in + 对象
,in
操作符会检查自有属性和原型属性;考虑性能问题,可以选择所有对象都拥有的hasOwnProperty()
方法,该方法在给定属性存在且为自有属性时返回ture
var ob = new Object();
ob.name = 'mnichangxin';
Object.prototype.age = 20; //原型添加属性
/* 属性探测 */
console.log('name' in ob); //true
console.log('age' in ob); //true
console.log(ob.hasOwnProperty('name')); //true
console.log(ob.hasOwnProperty('age')); //false
删除属性
使用delete
操作符:delete + 属性
,成功时返回true
/* 删除属性 */
delete ob.name;
console.log(ob.hasOwnProperty('name')); //false
属性枚举
可枚举属性的内部特征[[Enumerable]]
都被设置为true
,可以用for-in
循环遍历它们;如果只需要获取一个对象的属性列表数组,可以用Object.keys()
方法。它们的区别是:for-in
循环同时会遍历原型属性和自有属性,而Object.keys()
只返回自有属性
并不是所有属性都是可以枚举的。实际上,对象的大部分原生方法的[[Enumerable]]
特征都被设为false
。可以用propertyIsEnumerable()
方法检测一个属性是否为可枚举的
/* 属性枚举 */
//for-in循环
for(property in ob) {
console.log('Name:' + property);
console.log('Value:' + ob[property]);
}
//Object.keys()方法
var propertyList = Object.keys(ob);
for(var i = 0; i < propertyList.length; ++i) {
console.log('Name:' + propertyList[i]);
console.log('Value:' + ob[propertyList[i]]);
}
//判断是否可以枚举
console.log(ob.propertyIsEnumerable('name')); //true
属性类型
属性有两种类型:数据属性和访问器属性。
[[Put]]
方法的默认行为是创建数据属性;访问器属性不包含值而是定义了一个当属性被读取时调用的函数(称为getter
)和一个当属性被写入时调用的函数(称为setter
)。并不一定要同时定义getter和setter,可以选择定义其中之一(只读或只写)
属性特征
通用特征
有两个特征是数据和访问器属性都具有的。一个是[[Enumerable]]
,决定了你是否可以遍历该属性;另一个是[[Configurable]]
,决定该属性是否可以配置。声明的所有属性默认都是可枚举、可配置的
Object.defineProperty(ob, 'name', {
enumerable: false,
configurable: false
});
console.log(ob.propertyIsEnumerable('name')); //false
delete ob.name; //试图删除会报错
数据属性特征
数据属性拥有两个特有的特征:第一个是[[Value]]
,包含属性的值;第二个是[[Writable]]
,指示该属性是否可以写入,默认可写。当Object.defineProperty()
被调用时,首先检查属性是否存在。如果不存在,将根据属性描述对象指定的特征创建。用此方法时,一定要为所有的特征指定一个值,否则所有布尔类型默认设置为false
。
//数据属性特征值
Object.defineProperty(ob, 'name', {
value: 'world',
writable: true,
enumerable: true,
configurable: true
});
for(property in ob) {
console.log('Name:' + property);
console.log('Value:' + ob[property]);
}
访问器属性特征
访问器特有的属性特征:[[Get]]
和 [[Set]]
字面形式:
var person1 = {
_name:'MR',
}
get name() {
console.log('reading name');
return this._name;
}
set name() {
console.log('setting name to %s', value);
this._name = value;
}
对象形式:
//访问器属性特征值
ob._name = 'MR';
Object.defineProperty(ob, 'name', {
get: function() {
console.log('reading name');
return this._name;
},
set: function(value) {
console.log('setting name to %s', value);
this._name = value;
},
enumerable: true,
configurable: true
});
定义多重属性
可以为一个对象同时定义多个属性,使用Object.defineProperties()
方法,该方法接受两个参数:需要改变的对象和一个包含所有属性的对象
//定义多重属性
Object.defineProperties(ob, {
_name: {
value: 'world',
enumerable: true,
configurable: true,
writable: true
},
name: {
get: function() {
console.log('reading name');
return this._name;
},
set: function(value) {
console.log('setting name to %s',value);
this._name = value;
},
enumerable: true,
configurable: true
}
});
获取属性特征
使用Object.getOwnPropertyDescriptor()
方法,只可用于自有属性。接受两个参数:对象和属性名。如果属性存在,它会返回一个属性描述对象
//获取属性特征
var des = Object.getOwnPropertyDescriptor(ob, 'name');
console.log(des.enumerable);
console.log(des.configurable);
禁止修改对象
禁止扩展
第一种方法是用Object.preventExtensions
()创建一个不可扩展的对象。该方法只接受一个参数,就是传入的对象。可以用Object.isExtensible()
检查[[Extensionable]]
的值
//禁止扩展
Object.preventExtensions(ob);
ob.month = 4;
console.log(ob.month); //undefined
对象封印
第二种方法是用Object.seal()
方法封印一个对象。该方法被调用时,[[Extensible]]
的特征被设置为false
,其所有属性的[[Configurable]]
特征被设为false
,只能读写它的属性。可以用Object.isSealed()
判断一个对象是否被封印。
Object.seal(ob);
ob.month = 4;
console.log(ob.month); //undefined
对象冻结
第三种方法是用Object.freeze()
来冻结一个对象。被冻结对象是一个数据属性都为只读的被封印对象,被冻结对象无法解冻。可以用Object.isFrozen()
判断一个对象是否被冻结。
Object.freeze(ob);
console.log(ob.month);
console.log(ob.month); //undefined