### ECMAScript中的对象其实就是一组数据和功能的集合,Object的每个实例都具有下列的属性和方法。
#### 1、定义:无序属性的集合,其属性可以包含基本值、对象或者函数。
#### 2、声明对象的方法
```
// 1、实例声明,添加属性
let obj = new Object()
obj.name = 'zhang'
// 2、对象字面量声明
let obj2 = { name: 'wang' }
```
#### 3、属性类型
1)数据属性:包含一个数据值的位置,这个位置可以读取和写入值。有4个描述行为的特性。
* [[configurable]]:表示能都通过delete删除属性重新定义属性,能否修改特性,能否把属性修改为访问器属性。一旦定义为false,就不能再定义configurable和configurable特性,否则会报错。
* [[enumerable]]:表示能否用for-in循环返回属性。
* [[writable]]:表示能否修改属性的值。
* [[value]]:包含这个属性的数据值。
注:使用Object.defineProperty()方法,未设置configurable、writable、enumerable的时候默认为false
```
// 1、configurable,定义为false后,不能再定义改特性和enumerable特性,如果重新定义会报错
let obj0 = { name: 'zhang' }
Object.defineProperty(obj0, 'name', { configurable: false })
Object.defineProperty(obj0, 'name', { configurable: true }) // Error
Object.defineProperty(obj0, 'name', { enumerable: false } // Error
Object.defineProperty(obj0, 'name', { writable: false }) // 正常执行
Object.defineProperty(obj0, 'name', { value: 'wang' }) // 正常执行
delete obj0.name // 严格模式报错,非严格模式不执行
// 2、enumerable
let obj1 = { name: 'zhang', sex: 'man' }
for (let key in obj1) { console.log(key) } // name sex 正常返回
Object.defineProperty(obj, name, { enumerable: false })
for (let key in obj1) { console.log(key) } // sex 只返回最后一个属性
// 3、writable
let obj2 = { name: 'zhang' }
obj2.name = 'wang'
Object.defineProperty(obj2, 'name', { writable: false })
obj2.name = 'li' // 严格模式报错,非严格模式不执行
console.log(obj2.name) // wang
4、value
let obj3 = { name: 'zhang' }
Object.defineProperty(obj3, 'name', { value: 'wang' })
console.log(obj3) // { name: 'wang' }
```
2)访问器属性:不包含数据属性[[writable]]和[[value]]特性,但是包含getter和setter函数(不过不是必需的)。读取访问器属性时会调用getter函数,写入时调用setter函数。访问器属性不能直接定义,必须使用Object.defineProperty()来定义。有4个特性。
* [[configurable]]:表示能都通过delete删除属性重新定义属性,能否修改特性,能否把属性修改为数据属性。一旦定义为false,就不能再定义configurable和configurable特性,否则会报错。
* [[enumerable]]:表示能否用for-in循环返回属性。
* [[get]]:读取属性时调用的函数,默认值为undefined
* [[set]]:写入属性时调用的函数,默认值为undefined
```
let 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 = 2
}
}
})
book.year = 2003 // 没有效果
book.year = 2005
console.log(book) // {_year: 2005, edition: 2, year: 2005}
```
注:_year前面的下划线是一种常见的记号,用于表示只能通过对象方法访问的属性。
```
// __defineGetter__和__defineSetter__方法
let book = { year: 2004, edition: 1 }
book.__defineGetter__('year', function () {
return this._year
})
book.__defineSetter__('year', function (newVaule) {
if (newValue > 2005) {
this._year = newValue
this.edition = 2
}
})
book.year = 2003 // 没有效果
book.year = 2005
console.log(book) // {_year: 2005, edition: 2, year: 2005}
```
定义多个属性
```
let book = {}
Object.defineProperties(book, {
_year: {
writable: true,
value: 2004
},
edition: {
writable: true,
value: 1
},
year: {
get: function () {
return this._year
},
set: function (newValue) {
if (newValue > 2004) {
this._year = newVaule
this.edition = 2
}
}
}
})
book.year = 2003 // 没有效果
book.year = 2005
console.log(book) // {_year: 2005, edition: 2, year: 2005}
```
#### 4、读取属性的特性
使用Object.getOwnPropertyDescriptor(target, key)方法,可以获取给定属性的特性,返回一个对象。如果是数据属性,这个对象包含configurable、writable、enumerable、value四个属性;如果是访问器属性,这个对象包含configurable、enumerable、get、set四个属性。
```
let book = {}
Object.defineProperties(book, {
_year: {
writable: true,
value: 2004
},
edition: {
writable: true,
value: 1
},
year: {
get: function () {
return this._year
},
set: function (newValue) {
if (newValue > 2004) {
this._year = newValue
this.edition = 2
}
}
}
})
let descriptor0 = Object.getOwnPropertyDescriptor(book, '_year')
console.log(descriptor0) // { configurable: false, enumerable: false, writable: true, value: 2004 }
let descriptor1 = Object.getOwnPropertyDescriptor(book, 'year')
console.log(descriptor1.enumerable) // false
console.log(typeof descriptor1.set) // function
```
#### 5、工厂模式
对然Object构造函数或对象字面量都可以用来创建单个对象,但这种方法有明显的缺点,使用同一个接口创建很多对象的时候,会产生大量重读代码。这是后产生了工厂模式,一种函数,用函数来封装以特定接口创建对象的细节。
```
// 函数createPerson()能够接收参数来构建一个包含所有必要信息的Person对象。可以无数次调用这个函数创建一个包含两个属性和一个方法的对象。工厂模式虽然解决了创建多个相似对象的问题,却没有解决对象识别的问题(即怎样知道一个对象的类型)。
function createPerson (name, sex) {
var o = new Object()
o.name = name
o.sex = sex
o.sayName = function () {
alert(this.name)
}
return o
}
```
#### 6、构造函数模式
构造函数创建实例用new操作符,这种方式调用构造函数会经历4步:1、创建一个新对象。2、将构造函数的作用域赋值给新对象(因此this就指向了新对象)。3、执行构造函数的代码(为新对象添加属性)。4、返回新对象。
```
function Person (name, sex) {
this.name = name
this.sex = sex
this.sayName = function () {
alert(this.name)
}
}
let person0 = new Person('zhang', 'man')
let person0 = new Person('wang', 'woman')
// constructor属性标识当前构造函数
console.log(person0.constructor === Person) // true
// 也可以用instanceof检测对象类型
console.log(person0 instanceof Object) // true
console.log(person0 instanceof Person) // true
// 构造函数模式的问题:每个方法都要在每个实例上重新创建一遍,Person0与Person1的sayName方法不是同一个方法。
console.log(person0.sayName === person1.sayName) // false
```
注:创建自定义构造函数意味着将来可以将它的实例标识为一种特定的类型,这正是构造函数模式胜过工厂模式的地方。