前言
  1. 面向对象的语言都有一个的概念,通过类可以创建任意多个具有相同属性和方法的对象。
  2. JavaScript中把对象定义为无序属性的集合,属性可以包含基本值,对象或者函数。也可以将对象理解为一组没有特定顺序的值。
  3. 每个对象都是基于一个引用类型创建的。
一. 理解对象

之前说过创建对象的最简单的方式就是创建一个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

  • 访问器属性

访问器属性是不包含数据值的。它只包含两个函数,一个是gettersetter函数,不过这两个函数都不是必须的。在读取访问器属性的时候,会调用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'
总结一波

关于这些内容主要讲的就是:

  1. 在JavaScript的对象属性可以分为两类,一类就是数据属性,一类就是访问器属性。
  2. 数据属性有configurable, enumberable, wirtable, value四个特性。
  3. 访问器属性有configurable, enumberable, get, set四个特性。
  4. 访问器属性只能通过Object.defineProperty()方法进行设置。
  5. 还有Object.defineProperties()方法和Object.getOwnPropertyDescriptor()方法的作用。
    ...

fsrookie
2.9k 声望256 粉丝

目前很多文章都是摘抄记录其他教程。见谅。