JavaScript中创建自定义对象有两种方法。
一种是对象字面量法,例如:
var person ={ name:"xiaochang", age:24, sayName:function(){ alert(this.name); } }
另一种是用new运算符创建一个Object()的实例,然后再为其添加属性和方法。例如:
var person = new Object(); person.name = "xiaochang"; person.age = 24; person.sayName = function(){ alert(person.name); }
属性类型及其特性
JavaScript中定义了两种不同的属性:数据属性和访问器属性。数据属性一般用于存储数据值,而访问器属性一般进行get/set操作,不能直接存储数据值。在ES5中,为了描述属性(property)的各种特征,定义了4个描述其行为的特性(attribute)。在JavaScript中不能直接访问特性,要放在两对方括号中。
数据属性:
数据属性主要有四个特性描述其行为:
1、[[Configurable]]:默认为true。表示能够通过delete删除属性,能否修改属性特性,或把属性修改为访问器属性;
2、[[Enumerable]]:默认为true。表示能否通过for-in循环返回属性;
3、[[Writable]]:默认为true。表示能否修改属性的值;
4、[[value]]:默认为undefined。包含这个属性的数据值。读取和写入属性值时都从这个位置进行。
直接在对象上定义的属性,其数据特性默认如下:
1、[[Configurable]]:true
2、[[Enumerable]]:true
3、[[Writable]]:true
4、[[Value]]:“xiaochang"(初始时的赋值)
这些特性不能直接被访问,要修改属性的特性只能通过Object.defineProperty()方法。该方法包含三个参数:
属性所在的对象,属性的名字,一个描述符对象(包含上述四个特性的一个或多个)。
var person = { age:100 ; } Object.defineProperty(person,"name",function{ configurable:false; writable:false; value:"xiaochang"; }); Object.defineProperty(person,"tall",{ value:160; }); for (attr in perso){ console.log(attr); //name,age } console.log(person.name);//xiaochang person.name="CC"; //为name属性指定新值 console.log(person.name);//xiaochang delete person.name; //删除name属性 console.log(person.name);//xiaochang console.log(person.age); //100 person.age=200; //为age属性指定新值 console.log(person.age); //200 delete person.age; //删除age属性 console.log(person.age); //undefined console.log(person.tall); //160 person.tall=170; //修改tall属性的值 console.log(person.age); //160 delete person.tall; //删除tall属性 console.log(person.tall); //160
直接在对象上定义的属性,如age,[[Configurable]]、[[Enumerable]]、[[Writable]]特性都被设置为true。
属性name的[[Configurable]]、[[Writable]]被设置为false。所以无法修改和删除。
调用Object.defineProperty()方法时,如果不显示指定configurable、enumerable、writable的值,就默认为false。如属性tall。
需注意的是,当configurable设置为false后无法再将其改为true。且除了writable之外,无法修改其他特性。在configurable为true的情况下,可多次调用Object.definePro[erty()修改同一属性。
访问器属性:
包含getter和setter函数。读取访问器属性时,调用getter函数,返回有效的值;在写入访问器属性时,调用setter函数传入新值。它包含了4个特性:
1、[[Configurable]]:表示是否能通过delete删除属性从而重新定义属性,能否修改属性的特性,能否把属性修改为数据属性;
2、[[Enumerable]]:表示能否用for-in循环返回。
3、[[Get]]:读取属性时调用的函数,默认为undefined;
4、[[Set]]:写入属性时调用的函数,默认为undefinef。
getter和setter不一定要成对出现,只有getter函数表明该属性只可读不可写,只有setter函数表明该属性只可写不可读。
访问器属性无法直接定义,必须使用Object.defineProperty()来定义。
var person = { _name:"xiaochang", //name属性只可读不可写 _age:100, //age属性只可写不可读 _tel:123456 //tel属性可读可写 }; Object.defineProperty(person,"name",{ get:function(){ return this._name; } }); Object.defineProperty(person,"age",{ set:function(newage){ this._age=newage; } }) ; Object.defineProperty(person,"tel",{ get:function(){ return this._tel; }, set:function(newtel){ this._tel=newtel; } }); console.log(person.name); //"xiaochang" person.name="CC"; //尝试修改name属性 console.log(person.name); //"xiaochang" console.log(person.age); //undefined,不可读属性 person.age=200; //修改age console.log(person._age); //直接读取对象方法才能访问的属性。可以看到值已更新为200 console.log(person.tel); //123456 person.age=654321; //修改tel person.log(person.tel); //654321
属性前的下划线,表示只能通过对象方法访问的属性,当调用Person.name时实际调用了name属性的getter函数(直接调用person._name可得到相同的结果,这样做访问器就没有什么意义了)。为person.name赋值时调用的是name属性的setter函数。
定义多个属性:
ECMAScript 5还提供了一个对对象定义多个属性的方法,Object.defineProperties()。该方法接受两个参数:属性所在的对象,多个属性的名字和其描述符对象组成的对象。上面的例子可以改写成如下:
var person = { _name:"xiaochang", _age:100, _tel:123456 }; Object.defineProperties(person,{ name:{ get:function(){ return this._name; } }, age:{ set:function(newage){ this._age = newage; } }, tel:{ get:function(){ return this._tel; }, set:function(newtel){ this._tel = newtel; } } });
读取属性的特性:
对于上述属性特性,ECMAScript 5给出了可以取得给定属性的描述符的方法。
Object.getOwnPropertyDescriptor(),该方法包含两个参数:属性所在的对象,要读取器描述符的属性名称。方法返回一个对象,如果是访问器属性,返回的对象有configuable、enumerable、get和set;如果是数据属性,这个返回对象的属性包括configuable、enumerable、writable和value。如:
var descriptor = Object.getOwnPropertyDescriptor(person,"tel"); for (attr in descriptor){ console.log(attr+":"+descriptor[attr]); } //运行结果如下: get:function(){return this._tel;} set:function(newtel){this._tel=newtel;} enumerable:false configurable:false
数据属性和访问器属性的作用:
先看代码
var obj ={name:"percy"}; console.log(obj.name); //percy boj.name = "zyj"; console.log(obj.name); //zyj //这里借Math对象来举例 //首先说明一下,Math对象和上面定义的ogj对象都只是Object对象的一个实例 console.log(Math.PI); //3.1415926… Math.PI=1234; console.log(Math.PI); //3.1415926…
同样是Object的实例,obj对象的属性却可以被改写,而Math对象的属性却不能被改写。
这是因为,通过new运算符或对象字面量直接在对象上定义的属性,它们的[[Configurable]]、[[Enumerable]]、[[Writable]]特性默认都被设置为true。[[Value]]则被设定为指定的值。
如果要让Obj对象的属性也不能被改写,就要用到Object.defineProperty()方法。
若不按照上面2种方法为对象添加属性,而直接通过Object.defineProperty()为对象添加属性以及值,这种情况下,该对象的这个属性的另外3个特性的默认值都是false。
var person={}; //新建一个空对象 Object.defineProperty(person,"name",{ value:"perch" }); console.log(Object.getOwnPropertyDescriptor(person,"name")); //打印:Object{value:"percy",configurable:false,enumerable:false,writable:false}
访问器属性则是用来实现JavaScript引擎本身使用得,因此不建议开发者使用访问器属性。