前言
面向对象(OO)的语言有一个标志,那就是他们都有类的概念,而通过类可以创建任意多个具有相同属性和方法的对象。然而,ECMAScript中没有类的概念,因此它的对象也与基于类的语言中的对象有所不同。
ECMAScript把对象定义为:
无序属性的集合,其属性可以包含基本值、对象、函数。
每个对象都是基于一个引用类型创建的,这个引用类型可以是原生类型,也可以是开发人员自定义的类型。
理解对象
创建自定义对象的最简单方式就是创建一个Object的实例,然后再为它添加属性和方法,如下所示。
var person = new Object();
person.name = "sheldon";
person.age = 15;
person.sayHello = function(){
console.log(this.name)
}
值得关注的是:早期JS开发人员经常使用这种模式创建对象。几年后,对象字面量成为创建对象的首选模式
var person = {
name: "sheldon",
age:15,
sayHello:function(){
console.log(this.name);
}
}
这个例子的person对象与前面例子的person对象是一样的,有相同的属性和方法。
一、属性类型
ECMAScript中有两种属性:数据属性和访问器属性。
1、数据属性
数据属性包含一个数据值的位置:这个位置可以读取和写入值。数据属性还有4个描述其行为的特性。
- [[Configurable]]:表示能否通过delete删除属性,能否修改属性的特性。默认值是true。
- [[Enumerable]]:表示能否通过for-in循环返回属性。默认值是true。
- [[Writable]]:表示能否修改属性的值。默认值是true。
- [[Value]]:这个属性的数据值。默认值是undefined
对于像前面例子中那样直接在对象上定义的属性,他们的[[Configurable]]、[[Enumerable]]、[[Writable]]特性都默认是true,而[[Value]]特性被设置为指定的值“sheldon”
要修改属性默认的特性,必须使用ECMAScript5的Object.defineProperty()方法,使用方式如下。
var person = {};
Object.defineProperty(person,"name",{
writable:false,
value:"sheldon"
});
alert(person.name); // sheldon
person.name = "dog";
alert(person.name); // sheldon
上例属性的值“sheldon”是只读的了,严格模式下赋值会报异常,普通模式将忽视赋值操作。
另外,可以多次调用Object.defineProperty()方法修改同一属性,但是configurable的特性一旦设置为false之后,就不行了。而且一旦调用了Object.defineProperty()方法,[[Configurable]]、[[Enumerable]]、[[Writable]]特性就都默认是false了。Object.defineProperty()方法多数情况下用不上,但是理解这些概念对理解JavaScript对象非常有用。
IE8是第一个实现Object.defineProperty()方法的浏览器版本,然而实现存在诸多限制。建议不要在IE8中使用该方法
2、访问器属性
访问器属性不包含数据值,而是包含一对儿getter和setter函数。它还有4个特性
- [[Configurable]]:表示能否通过delete删除属性,能否修改属性的特性,能否把属性修改为数据属性。默认值是true。
- [[Enumerable]]:表示能否通过for-in循环返回属性。默认值是true。
- [[Get]]:在读取属性时调用的函数。默认值是undefined。
- [[Set]]:在写入属性时调用的函数。默认值是undefined。
访问器属性不能直接定义,必须使用Object.defineProperty()来定义。请看下面的例子
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
以上代码创建一个book对象,并给它定义两个默认的属性:_ year和edition。_ year前面的下划线是一种常用的记号,用于表示只能通过对象方法访问的属性。而访问器属性year则包含一个getter函数和一个setter函数。getter函数返回_ year的值,setter函数通过计算来确定正确的版本。因此,把year属性修改为2005会导致_year变成2005,而edition变为2。这是使用访问器属性的常见方式,即设置一个属性的值会导致其他属性发生变化。
不一定非要同时指定getter和setter。只指定getter意味着属性不能写,严格模式报错,非严格模式忽略。
支持ECMAScrpit5的这个方法的浏览器有IE9+(IE8只是部分实现)和其他正常浏览器。
二、定义多个属性
由于为对象定义多个属性的可能性很大,E5又定义了一个Object.defineProperties()方法,例如:
var book = {};
Object.defineProperties(book,{
_year:2004,
edition:1,
year:{
get: function(){
return this._year;
},
set: function(newValue){
if(newValue > 2004){
this._year = newValue;
this.edition += newValue -2004;
}
}
}
});
支持该方法的的浏览器 IE9+和其他。
三、读取属性的特性
E5的Object.getOwnPropertyDescriptor()方法,可以取得给定属性的描述符。该方法有两个参数:属性所属对象,要读取描述符的属性名称。返回一个对象,如果是访问器属性,该对象有属性configurable、enumerable、get、set;如果是数据属性,该对象有属性configurable、enumerable、writable、value。
举例就略了,读者自己尝试。Js中针对任何对象都可以使用此方法,包括BOM、DOM对象。支持浏览器IE9+和其他。