前言
- 面向对象的语言都有一个类的概念,通过类可以创建任意多个具有相同属性和方法的对象。
- JavaScript中把对象定义为无序属性的集合,属性可以包含基本值,对象或者函数。也可以将对象理解为一组没有特定顺序的值。
- 每个对象都是基于一个引用类型创建的。
一. 理解对象
之前说过创建对象的最简单的方式就是创建一个Object实例,然后为它添加属性和方法:
var person = new Object()
person.name = 'Nicholas'
person.age = '29'
person.sayName = function () {
alert(this.name)
}
如上,创建了一个名为person的对象,然后为它添加了两个属性和一个方法。
还有一种是就是通过对象字面量的方式创建对象,如下:
var person = {
name: 'Nicholas',
age: 29,
sayName: function(){
alert(this.name)
}
}
1. 属性类型
JavaScript第五版在定义属性内部才用的特性,描述了属性的各种特征。定义这些特性是为了给JavaScript引擎用的,因此在JavaScript中不能直接访问它们。为了表示特性是内部值,该规范把它们放在了两对儿方括号中,例如:[[Enumerable]]。
JavaScript中有两种属性:数据属性和访问器属性。
- 数据属性
数据属性包含一个数据值的位置,在这个位置可以读取和写入值。数据属性有4个描述其行为的特性:
(1)[[Configurable]]: 表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。对于直接定义在对象上的数据属性,该特性为true。
(2 [[Enumerable]]: 表示能否通过for-in循环遍历到属性。
(3)[[Writable]]: 表示能否修改属性的值。
(4)[[Value]]: 包含整个属性的数据值,读取属性的时候,从这个位置进行读取,写入属性值得时候,保存到该位置。
对于直接在对象上添加的属性,他们的[[Configurable]]、[[Enumerable]]、[[Writable]]特性都被设置为true。如:
var person = {
name: 'Nicholas'
}
如上,我们通过对象字面量的方法创建一个对象,创建一个name属性,为它指定的值为”Nicholas”, 那么此时[[Value]]特性将被设置为’Nicholas’,而对这个值得任何修改都将反应在这个位置上。其他的三个特性都默认为true。
要修改属性默认的特性可以使用Object.defineProperty()方法。这个方法接受三个参数:属性所在对象,属性的名字,一个描述符对象。其中描述符对象的属性必须是:configurable, enumerable, writable, value,设置其中一个或多个值,可以修改对应的特性。如下:
var person = {}
Object.defineProperty(person, 'name', {
wirtable: false,
value: "Nicholas"
})
console.log(person.name) //'Nicholas'
person.name = 'greg'
console.log(person.name) //'Nicholas'
如上,创建一个名为name的属性,将他的值设置为’Nicholas’, 然后设置为只读,这个属性的值是不可修改的。类似的规则也适用于不可配置的属性:
var person = {}
Object.defineProperty(person, 'name', {
configurable: false,
value: 'Nicholas'
})
console.log(person.name) //'Nicholas'
delete person.name
console.log(person.name) //'Nicholas'
如上,把configurable设置为false,表示不能从对象中删除属性,所以看到删除name属性并没有起作用。而且,如果把属性定义为不可配置的,就不能把它变回可配置的了,此时再调用Object.defineProperty()方法除了只能修改writable,其他都会导致错误。
var person = {}
Object.defineProperty(person, 'name', {
configurable: false,
value: 'Nicholas'
})
Object.defineProperty(person, 'name', {
configurable: true,
value: 'Nicholas'
})
//报错:Cannot redefine property: name...
如上就会报错, 也就是多次调用Object.defineProperty()方法修改同一属性,但是把configurable特性设置为false之后就会有限制。
通过Object.defineProperty()创建一个新属性的时候,如果不指定configurable,enumberable 和writable特性的默认值都是false。
- 访问器属性
访问器属性是不包含数据值的。它只包含两个函数,一个是getter和setter函数,不过这两个函数都不是必须的。在读取访问器属性的时候,会调用getter函数,这个函数返回该属性有效的值。在写入访问器属性的时候,会调用setter函数并传入新值,这个函数负责决定如何处理数据。访问器属性也具有4个特性:
(1)[[Configurable]]: 表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为数据属性,对于直接在对象上定义的属性,这个特性的默认值为true。
(2)[[Enumberable]]: 表示能否通过for-in循环返回属性。对于直接在对象上定义的属性,该特性默认为true。
(3)[[Get]]: 在读取属性时调用的函数,默认为undefined
(4)[[Set]]: 在写入属性时调用的函数,默认值为undefined
访问器属性不能直接定义,必须使用Object.defineProperty()来定义。如下例子:
var book = {
_year: 2004,
editor: 1
}
Object.defineProperty(book, "year", { //定义了一个访问器属性year
get: function() {
return this._year
},
set: function(newVal) {
if(newVal > 2004) {
this._year = newVal
this.editor = this.editor + newVal - 2004
}
}
})
book.year = 2005
console.log(book.year) //2005
console.log(book.editor) //2
如上,我们创建一个book对象,并且定义了两个默认的属性_year和editor。访问器属性则包含一个getter函数和一个setter函数。getter函数返回_year的值,而setter函数通过计算来确定正确的版本。使用访问器属性的常见方式,即设置一个属性的值会导致其他属性发生变化。
不一定要同时制定getter和setter。只指定getter以为着是不能写,尝试写入会忽略,在严格模式下会报错。同理,只设置setter函数的访问器属性不能读。
在此之前要创建访问器属性有两个非标准的方法,再这里稍微提一下:__defineGetter__和__defineSetter__。
var book = {
_year: 2004,
editor: 1
}
book.__defineGetter__("year", function(){
return this.year
})
book.__defineSetter__("year", function(newval) {
if(newval > 2005) {
this._year = newval
this.editor += newval - 2004
}
})
2. 定义多个属性
ECMAScript5 定义了一个Object.defineProperties()方法,利用这个方法可以通过描述符一次定义多个属性。这个方法接受两个参数:第一个对象是要添加和修改其属性的对象,第二个对象的属性与第一个对象中要添加和修改的属性一一对应。如下例子:
var book = {}
Object.defineProperties(book, {
_year: {
writable: true,
value: 2004
},
editor: {
writable: true,
value: 1
},
year: {
get: function() {
return this._year
},
set: function(newval) {
if(newval > 2005) {
this._year = 2005
this.editor += newval - 2004
}
}
}
})
如上,定义两个两个数据属性_year和editor,和一个访问器属性year。
3. 读取属性的特性
在ECMAScript5 中定义了一个Object.getOwnPropertyDescriptor()方法,可以取得给定属性的描述符,这个方法接受两个参数: 属性所在对象和要读取其描述符的属性名称。其返回值是一个对象,如果是访问器属性,这个对象有confiurable, enumerable, get, set。如果是数据属性那么就是configurable, enumberable, writeable, value。
var book = {}
Object.defineProperties(book, {
_year: {
writable: true,
value: 2004
},
editor: {
writable: true,
value: 1
},
year: {
get: function() {
return this._year
},
set: function(newval) {
if(newval > 2005) {
this._year = 2005
this.editor += newval - 2004
}
}
}
})
var descriptor = Object.getOwnPropertyDescriptor(book, "_year");
console.log(descriptor.value) //2004
console.log(descriptor.configurable) //false
console.log(typeof descriptor.get) //'undefined'
var des = Object.getOwnPropertyDescriptor(book, 'year')
console.log(des.value) //'undefined'
console.log(des.enumberable) //false
console.log(typeof descriptor.get) //'function'
总结一波
关于这些内容主要讲的就是:
- 在JavaScript的对象属性可以分为两类,一类就是数据属性,一类就是访问器属性。
- 数据属性有configurable, enumberable, wirtable, value四个特性。
- 访问器属性有configurable, enumberable, get, set四个特性。
- 访问器属性只能通过Object.defineProperty()方法进行设置。
- 还有Object.defineProperties()方法和Object.getOwnPropertyDescriptor()方法的作用。
…