## 对象概述 面向对象(Object-Oriented,OO)的语言有一个标志,那就是它们都有类的概念,而通过类可以创建任意多个具有相同属性和方法的对象。但是,ECMAScript中并没有类的概念,所以它的对象也有有所不同。 ECMAScript对象是一个无序属性的集合,其属性可以包含基本值、对象或函数。 对象的每个属性或方法都有一个名字,而每个名字都映射到一个值。 每个对象都是基于一个引用类型创建的。 **1、对象** **1.1 创建对象** (1)创建自定义对象的最简单方式就是创建一个Object的实例,然后给其添加属性和方法: ``` var person = new Object(); person.name = 'tg'; person.age = 10; person.say = function(){ console.log(this.name); } ``` 上面的例子创建了一个名为person的对象,并为它添加了两个属性(name、age)和一个方法(say())。 (2)对象字面量 ``` var person = { name: 'tg', age: 10, say: function(){ console.log(this.name); } } ``` 这个person对象和上面例子是等价的。 **1.2 属性类型** ECMA-262第5版在定义只有内部才用的特性(attribute)时,描述了属性(property)的各种特征。ECMA-262定义这些特性是为了实现JavaScript引擎用的,因此在JavaScript中不能直接访问它们。该规范将它们放在了两对方括号中,表示特性是内部值,如[[Enumerable]]。 ECMAScript中有两种属性:数据属性和访问权属性 **1.2.1 数据属性** 数据属性包含一个数据值的位置,在这个位置可以读取和写入值。 数据属性有4个描述特性: - [[Configurable]]:表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。比如直接在对象上定义的属性,它们的这个特性默认值为true。 - [[Enumerable]]:表示能否通过for-in循环返回属性。对于直接在对象上定义的值,默认为true。 - [[Writable]]:表示能否修改属性的值。 - [[Value]]: 包含这个属性的数据值。读取属性值时,从这个位置读;写入属性值时,把新值保存在这个位置。默认值为undefined。 要修改属性默认的特性,必须使用ECMAScript 5的`Object.defineProperty()`方法,这个方法接收三个参数:属性所在的对象、属性的名字和一个描述符对象。其中,描述符对象的属性必须是:configurable、enumerable、writable和value。 ``` var person = {}; Object.defineProperty(person, 'name', { writable: false, value: 'tg' }); console.log(person.name); // "tg" person.name = 'tg2'; console.log(person.name); // "tg" ``` 在上面的例子中,我们将person对象中的名为name的属性的`writable`设置为false,也就是不可修改,所以即使后面执行了person.name='tg2',最后person对象的name值依旧是原始值。 注意:在严格模式下,如果对一个不可修改的属性执行赋值操作,会抛出错误;非严格模式下则忽略赋值操作。 一旦将configurable特性设置为false后,就不能再把它变回可配置的了,如果再修改除writable之外的特性,都会导致错误。 **1.2.2 访问器属性** 访问器属性不包含数据值,它们包含一对getter和setter函数(非必需)。在读取访问器属性时,会调用getter函数,返回有效的值;在写入访问器属性时,会调用setter函数并传入新值,它负责决定如何处理数据。 访问器属性: - [[Configurable]]:表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。比如直接在对象上定义的属性,它们的这个特性默认值为true。 - [[Enumerable]]:表示能否通过for-in循环返回属性。对于直接在对象上定义的值,默认为true。 - [[Get]]:在读取属性时调用的函数,默认为undefined - [[Set]]:在写入属性时调用的函数,默认为undefined 访问器属性不能直接定义,也是要使用`Object.defineProperty()`方法来定义。 ``` var person = { _age: 20, intro: '' }; Object.defineProperty(person, 'age', { get: function() { return this._age; }, set: function(newValue){ if(newValue < 18) { this.intro = '装嫩'; this._age = newValue; } } }); console.log(person.age); // 20 person.age = 17; console.log(person.intro); // "装嫩" ``` 在上面的例子中,访问器属性age有一个getter函数和一个setter函数。getter函数返回`_age`的值,setter函数通过判断`age`的设置值来改变其他属性值。 **1.2.3 定义多个属性** ECMAScript 5提供的`Object.defineProperties()`方法可以通过描述符一次定义多个属性,这个方法接收两个对象参数,第一个对象是要添加和修改其属性的对象,第二个对象的属性与第一个对象要添加或修改的属性一一对应。 ``` var person = {}; Object.defineProperties(person, { _age: { value: 20 }, intro: { value: '' }, age: { get: function(){ return this._age; }, set: function(newValue){ if(newValue < 18) { this.intro = '装嫩'; this._age = newValue; } } } }); ``` **1.2.4 读取属性的特性** ECMAScript 5提供的`Object.getOwnPropertyDescriptor()`方法可以取得给定属性的描述符,它接受两个参数:属性所在的对象和要读取其描述符的属性名称,返回来的是一个对象,如果是访问器属性,这个对象的属性有configurable、enumerable、get和set;如果是数据属性,这个对象的属性有configurable、enumerable、writable和value。 ``` var person = { _age: 20, intro: '' }; Object.defineProperty(person, 'age', { get: function() { return this._age; }, set: function(newValue){ if(newValue < 18) { this.intro = '装嫩'; this._age = newValue; } } }); var descriptor = Object.getOwnPropertyDescriptor(person,'age'); console.log(descriptor.enumerable); // false console.log(typeof descriptor.get); // "function" ``` 在JavaScript中,可以针对任何对象--包括DOM和BOM对象,使用`Object.getOwnPropertyDescriptor()`方法。