引言:尽管javascript里有大量内建引用类型,很可能你还是会频繁创建自己的对象。javascript编程一大重点就是管理那些对象。
3.1 定义属性
有两种创建自己的对象的方式:使用Object构造函数或者使用对象的字面形式。
你可以在对象定义之后,立即给它添加属性,也可以改变属性的值。你定义的对象总是由你随意修改。
当一个属性第一次被添加给对象时,javascript在对象上调用一个名为[[put]]的内部方法。[[put]]方法会在对象上创建一个新节点来保存属性,就像第一次在哈希表上添加一个键一样。当属性在每个对象上第一次被定义时,[[put]]方法都在该对象上被调用了。调用[[put]]的结果是在对象上创建了一个自有属性。一个自有属性表明仅仅该指定的对象实例拥有该属性。自有属性区别于原型属性。
当一个已有的属性被赋予一个新值时,调用一个名为[[set]]的内部方法。该方法将属性的当前值替换为新值。
3.2 属性探测
检查对象是否已经有一个属性,使用in操作符。in操作符在给定对象中查找一个给定名称的属性,如果找到返回true.
方法是值为函数的属性,所以也可以用in操作符来检查一个方法是否存在。
in操作符会检查自有属性和原型属性,而hasOwnProperty()方法仅检查自有属性。
var person={
name:"hui",
sayName:function(){
console.log(this.name);
}
};
console.log("name" in person); //true
console.log(person.hasOwnProperty("name")); //true
console.log("toString" in person); //true
console.log(person.hasOwnProperty("toString")); //false
name是person 的一个自有属性,而toString()方法是一个所有对象都具有的原型属性。
3.3 删除属性
(1)设置null并不能从对象中彻底移除那个属性。只是调用[[set]]将null 值替换了该属性原来的值而已。
(2)需要delete操作符彻底移除一个对象的属性。delete操作符针对单个对象属性调用名为[[Delete]]的内部方法。你可以认为该操作在哈希表中移除了一个键值对。delete操作符仅对自有属性起作用,它无法删除一个原型属性。
3.4 枚举属性
所有你添加的属性默认可枚举,可以用for in 遍历要他们。可枚举属性的内部特征[[Enumberable]]都被设置为true。
如果只需要获取一个对象的属性列表,ECMAScript5 引入了Object.keys()方法,它可以获取可枚举属性名字的数组。
for in和Object.keys()返回的可枚举属性有一个区别:for in循环会遍历原型属性和自有属性,而Object.keys()方法只返回自有属性。实际上,对象的大部分原生方法的[[Enumberable]]都被设置为false。
可以用propertyIsEnumberable()方法检查一个属性是否为可枚举的。
3.5 属性类型
数据属性: 包含一个值
访问器属性: 不包含值,而是定义了一个当属性被读取和属性被写入时调用的函数(getter/setter)
[[put]]属性的默认行为是创建数据属性。
var person={
_name:"hui",
get name(){
console.log("reading me");
return this._name;
},
set name(value){
console.log("setting name to ",value);
this._name=value;
}
};
console.log(person.name);
person.name="xia";
console.log(person.name);
本例定义了一个访问器属性name.一个数据属性_name保存访问器属性的实际值。getter被期望返回一个值,setter接受一个需要被赋给属性的值作为参数。
运行结果如下:
3. 6 属性特征
通用特征:[[Enumberable]] [[Configurable]](是否可配置)你声明的所有属性默认都是可枚举、可配置的。
如果想改变属性特征,可以使用Object.defineProperty()方法。该方法接收三个参数:拥有该属性的对象,属性名,包含需要设置的特征的属性描述对象。属性描述对象具有和内部特征同名的属性,但名字中不包含中括号。
var person={
name:"hui"
};
Object.defineProperty(person,"name",{
enumerable:false
});
console.log("name" in person); //true
console.log(person.propertyIsEnumerable("name")); //false
var properties=Object.keys(person);
console.log(properties.length); //0
Object.defineProperty(person,"name",{
configurable:false
});
delete person.name;
console.log("name" in person); //true
console.log(person.name); //hui
Object.defineProperty(person,"name",{
configurable:true
}); //error!
由于name属性不可配置,试图删除name将会失败,所以name依然存在于person 中。后面几行代码试图重新定义name为可配置的,然而这将抛出错误。
运行结果如下:
(1)数据属性特征
[[Value]] 包含属性的值 [[Writable]] 布尔值,指示该属性是否可以写入(所有的属性默认可写)
以下两段代码是同样的效果。
var person{
name:"hui"
};
var person={};
Object.defineProperty(person,"name",{
value:"hui",
enumerable:true,
configurable:true,
writable:true
});
当defineProperty()被调用时,它首先检查属性是否存在。不存在则根据属性描述对象指定的特征创建。当用defineProperty()定义新的属性时记得为所有的特征指定一个值,否则布尔型的值会默认设备false.
var person={};
Object.defineProperty(person,"name",{
value:"hui"
});
console.log("name" in person); //true
console.log(person.propertyIsEnumerable("name")); //false
delete person.name;
console.log("name" in person); //true
person.name="xia";
console.log(person.name); //"hui"
上例除了读取name属性的值,无法对他作任何事情。运行结果如下:
(2)访问器属性特征
[[get]] 内含getter函数 [[set]] 内含setter函数。和数据属性一样,可以指定访问器属性是否为可配置、可枚举。
var person={
_name:"hui",
get name(){
console.log("reading me");
return this._name;
},
set name(value){
console.log("setting name to ",value);
this._name=value;
}
};
上述代码可以写为如下形式:
var person={
_name:"hui"
};
Object.defineProperty(person,"name",{
get: function(){
console.log("reading me");
return this._name;
},
set: function(value){
console.log("setting name to ",value);
this._name=value;
},
enumerable:true,
configurable:true
});
上述代码中,get 和set关键字,包含函数的数据属性。设置[[Enumerable ]] [[和Configurable ]]可以改变访问器属性的工作方式。假如name属性只有一个getter的访问器属性,没有setter,它的值职能被读取,不能被改变。
3.6.4 定义多重属性
Object.definePropertise() 可以为一个对象同时定义多个属性。这个方法接受两个参数,需要改变的对象,和一个包含所有属性信息的对象。
3.6.5 获取属性特征
Object.getOwnPropertiseDescriptor() 可以获取属性的特征。这个方法只可用于自有属性。他接受两个参数:对象和属性名。如果属性存在,它会返回一个属性描述对象,内含四个属性:[[Enumerable ]] [[和Configurable ]],另外两个属性则根据属性类型决定。
var person={
name:"hui"
};
var descripter=Object.getOwnPropertiseDescriptor(person,"name");
console.log(descripter.enumerable);
console.log(descripter.configurable);
console.log(descripter.writable);
console.log(descripter.value);
运行结果如下:
3.7 禁止修改对象
禁止扩展 | Object.preventExtensions() 接受一个参数 | 对象不可扩展,不能再给它添加新方法和新属性 |
对象封印 | Object.seal() 接受一个参数 | 对象不可扩展且其所有属性都不可配置。不能给他添加新属性,也不能删除属性或改变 其类型(从数据属性变为访问器属性或相反) |
对象冻结 | Object.freeze()接受一个参数 | 对象不可扩展且被封印,也不能改变属性类型,不能修改和写入任何数据属性 |
用Object.isExtensible()来检查[[Extensible]]的值。用Object.isSealed()判断一个对象是否被封印。用Object.isFrozen()来判断一个对象是否被冻结。
确保对被封印对象使用严格模式。