在前面已经有稍微介绍过【 javascript引用类型之object类型 】,今天就具体的说说什么是对象。
在javascript中,对象就是一组无序的名/值对的集合。并且,对象的操作,是对其引用的操作。即,将一个对象赋给一个变量x,实际上也只是将该对象的引用赋给x;倘若又将变量x赋给另一个变量y,那么也只是将y指向x所指向的那个对象。
在【 javascript引用类型之object类型 】中有介绍过创建Object类型实例的方法。对象实例都拥有属性和方法。其实从某种意义上来说,方法也是属性,只不过他的值是个函数。
属性类型
在javascript中,属性可以分为两种,一种是数据属性,另一种是访问器属性。
每个属性都又都包含一个名字和四个特性。
数据属性
对于数据属性而言,它包含了一个数据值的位置,它的值也是其特性之一。因此,它的四个特性分别为:
- 可配置性(configurable):表示能否通过delete来删除属性,能否修改属性的特性,能否将其修改为访问器属性。若能,则值为true;若不能,则值为false。默认情况下,为true。
- 可枚举性(enumerable):表示能否通过for/in循环来返回属性。若能,则为true;若不能,则为false。默认情况下为true。
- 可写性(writable):表示能否修改属性的值。若能,则为true;若不能,则为false。默认为true。
- 值(value):表示这个属性包含的数据值。读取数据属性时,从这个位置读;写入属性值时,将新值保存在这个位置。
访问器属性
访问器属性不包含数据值。它包含一对getter和setter函数,但是这两个函数不是必须的。当程序查询存取器属性值时,javascript就调用getter()方法,返回属性存取表达式的值;当程序设置存取器属性的值时,javascript就调用setter()方法,将赋值表达式右侧的值当作参数传入setter()。
前面提到,getter()和setter()方法不是必须的。当它同时具有这两个方法时,它是可读/写的;当它只有getter()方法时,它是只读的;当它只有setter()方法时,它是只写的。
存取器属性也同样拥有四个特性,它们分别是:
- 可配置性(configurable):同数据属性。
- 可枚举性(enumerable):同数据属性。
- get:在读取属性时调用的函数。默认值为undefined。
- set:在写入属性时调用的函数。默认值为undefined。
定义属性
在ECMAscript5中,可以用Object.defineProperty()方法来修改属性默认的特性值。这个方法有三个参数:属性所在的对象,属性的名字和一个描述符对象。其中,描述符对象的属性必须是属性的特性名。
//数据属性
var o={};
Object.defineProperty(o,"x",{value:1,writable:true,enumerable:false,configurable:true});
alert(o.x); //1
o.x=2;
alert(o.x); //2
alert(o.propertyIsEnumerable("x")); //false
delete o.x;
alert(o.x); //undefined
Object.defineProperty(o,"y",{value:4,writable:false,enumerable:true,configurable:false});
alert(o.y); //4
o.y=44;
alert(o.y); //4
alert(o.propertyIsEnumerable("y")); //true
delete o.y;
alert(o.y); //4
//访问器属性
var book={__year:2004,edition:1};
Object.defineProperty(book,"year",{
get:function(){
return this.__year;
},
set:function(newValue){
if(newValue>2004){
this.__year=newValue;
this.edition+=newValue-2004;
}
}
});
book.year=2005;
alert(book.edition); //2
注意:一旦把属性定义为不可配置的,就不能再将其设置为可配置的了。
定义多个属性
ECMAscript5还定义了一个Object.defineProperties()方法,用于定义多个属性。这个方法有两个参数:第一个参数是属性所在的对象,第二个参数是一个映射表:
var o={};
Object.defineProperties(o,{ "x":{value:1,writable:true,enumerable:false,configurable:true},
"y":{value:4,writable:false,enumerable:true,configurable:false}
});
alert(o.x); //1
o.x=2;
alert(o.x); //2
alert(o.propertyIsEnumerable("x")); //false
delete o.x;
alert(o.x); //undefined
alert(o.y); //4
o.y=44;
alert(o.y); //4
alert(o.propertyIsEnumerable("y")); //true
delete o.y;
alert(o.y); //4
在不支持前面提到的两个方法的浏览器中,将不能修改属性的可配置性和可枚举性。
读取属性的特性
在ECMAscript5中,Object.getOwnPropertyDescriptor()方法可以取得给定属性的描述符。返回的是一个对象,如果是访问器属性,该对象的属性有:configurable、enumerable、get和set;如果是数据属性,该对象的属性有:configurable、enumerable、writable和value。
var o={x:1};
alert(Object.getOwnPropertyDescriptor(o,"x").value); //1
alert(Object.getOwnPropertyDescriptor(o,"x").writable); //true
alert(Object.getOwnPropertyDescriptor(o,"x").enumerable); //true
alert(Object.getOwnPropertyDescriptor(o,"x").configurable); //true
这个方法对于继承或者不存在的属性都将返回undefined:
var o={x:1};
alert(Object.getOwnPropertyDescriptor(o,"y")); //undefined
alert(Object.getOwnPropertyDescriptor(o,"toString")); //undefined
属性查询和设置
其实前面也有提到过了,可以通过.或者[]来访问对象的属性:
var o1={x:1,y:2};
alert(o1.x); //1
alert(o1["y"]); //2
注意,当以[]来访问属性时,[]内必须以字符串的形式出现。
也可以通过.和[]来设置属性的值:
var o={};
o.x=1;
o["y"]=2;
alert(o.x); //1
alert(o.y); //2
用[]来访问属性时,看起来像是数组。其实,由此也可以看出,javascript中的对象都是关联数组。
删除属性
在javascript中,用delete可以删除对象的属性,并且只能删除对象的自有属性或可配置性为true的属性。至于什么是可配置型,下面会提到。
var o={x:1,y:2};
alert(o.x); //1
delete o.x;
alert(o.x); //undefined
delete运算符只是断开属性与其宿主对象的关系,并不会去操作属性中的属性:
var p={z:1};
var o={x:1,y:p};
alert(o.y); //[object Object]
alert(p.z); //1
delete o.y;
alert(o.y); //undefined
alert(p.z); //1
检测属性
in运算符、hasOwnProperty()和propertyIsEnumerable()方法都可以用来检测属性,不过各自的检测机制不同。
in运算符
in运算符用来检查对象是否包含属性,如果有,不管是自有属性还是继承属性,都将返回true。
var o={x:1,y:2};
alert("x" in o); //true
alert("z" in o); //false
alert("toString" in o); //true
hasOwnProperty()
hasOwnProperty()方法用来检测属性是否是自有属性,如果是,返回true;如果不是,则返回false。
var o={x:1,y:2};
alert(o.hasOwnProperty("x")); //true
alert(o.hasOwnProperty("toString")); //false
propertyIsEnumerable()
propertyIsEnumerable()方法用于检测属性是否是可枚举的。默认情况下,自有属性都是可枚举的,而内置属性则是不可枚举的。
var o={x:1,y:2};
alert(o.propertyIsEnumerable("x")); //true
alert(o.propertyIsEnumerable("toString")); //false
枚举属性
用for/in循环可遍历对象中所有的可枚举属性。前面有提到过,默认情况下,自有属性是可枚举的,内置属性是不可枚举的。但是,属性有个特性可以改变属性的可枚举性(下面会提到)。那么,假如只想获得可枚举的自有属性,并且不包括方法,应该怎么操作呢?
可结合前面提到过的方法来过滤掉一些我们不想得到的属性:
过滤继承属性:
for(p in o){
if(!o.hasOwnProperty(p)) continue;
}
过滤方法:
for(p in o){
if(typeOf o[p]==="function") continue;
}
注意,这边属性是通过[]来访问的,因为我们不知道对象拥有那些属性。这正是用[]来访问属性的重要用途。
在ECMAscript5中,还提供了一种方法来获得对象上所有的可枚举的实例属性。该方法接收一个对象作为参数,返回一个包含该对象中所有可枚举属性的字符串数组。
var o={x:1,y:2};
alert(Object.keys(o)); //x,y