深刻理解对象的数据属性与访问器属性

建立对象的方式有两种:第一种,经过new操做符后面跟Object构造函数,第二种,对象字面量方式。以下函数

var person = new Object(); person.name = 'Nicy'; person.age = 21; person.sayName = function() { console.log(this.name); }; var person = { name: 'Nicy', age: 21, sayName: function() { console.log(this.name); } }

这两种方式建立出来的对象是同样的,有相同的属性和方法。这些属性内部都有描述其行为的属性描述符。this

Object.defineProperty() 

经过Object.defineProperty() 能够直接在对象上建立一个属性,也能够修改已有的属性。spa

Object.defineProperty(obj, prop, descriptor) 接收三个参数:code

  obj:属性所在的对象  对象

  prop:要访问的属性名blog

  descriptor:描述符对象递归

描述符对象包含六个属性:configurable、enumerable、writable、value、get、set ,要修改属性的特性,必须使用Object.defineProperty()方法。ip

经过以上两种方式添加的对象属性,其布尔值特性默认值是true,经过Object.defineProperty来修改属性特性时,只设置须要修改的特性便可;而经过Object.defineProperty建立的属性,其布尔值特性默认值是false。内存

ECMAScript中属性分为两种:数据属性和访问器属性。get

数据属性

数据属性包含四个属性描述符:

[[Configurable]] : 表示可否经过delete删除属性从而从新定义属性,可否修改属性特性,可否把属性修改成访问器属性。经过以上方式添加的对象属性,默认为true。

[[Enumerable]] : 表示可否经过for-in 循环访问属性。经过以上方式添加的对象属性,默认为true。

[[Writable]] : 表示可否修改属性的值。经过以上方式添加的对象属性,默认为true。

[[Value]] : 包含这个属性的数据值,可读取写入。经过以上方式添加的对象属性,默认为undefined。

Writable

var person = {}; Object.defineProperty(person, "name", { value: 'Nicy' }) person.name = 'Lee';   console.log(person.name) // 'Nicy'
 Object.defineProperty(person, "name", { writable: true }) person.name = 'Lee'; console.log(person.name) // 'Lee'

Object.defineProperty直接建立的属性writable默认为false,value值不可修改,此时修改name为Lee,在非严格模式下不会报错,但操做被忽略,在严格模式下会报错。

Configurable

var person = { name: 'Nicy', age: 21, sayName: function() { console.log(this.name); } } Object.defineProperty(person, "name", { configurable: false }) delete person.name;    // 操做被忽略,没法经过delete删除属性
Object.defineProperty(person, "name", { // throw error configurable:true })
Object.defineProperty(person,
"name", { // throw error enumerable: false })
Object.defineProperty(person,
"name", { // 因为writable为true,因此能够修改value value: 'Lucy' })
console.log(person.name)
// Lucy

Object.defineProperty(person, "name", { // writable可进行true -> false的单向修改 writable: false })
Object.defineProperty(person,
"name", { // throw error value: 'Lee' })
Object.defineProperty(person,
"name", { // throw error,此时writable不能够false -> true writable: true })

总结一下configurable:当configurable设为false时,

  一、不能够经过delete去删除该属性从而从新定义属性;

  二、不能够转化为访问器属性;

  三、configurable和enumerable不可被修改;

  四、writable可单向修改成false,但不能够由false改成true

  五、value是否可修改根据writable而定。

当configurable为false时,用delete删除该属性,在非严格模式下,不会报错,但操做被忽略,在严格模式下会报错;其余不可被修改的特性修改时会报错。

Enumerable

enumerable表示对象属性是否能够在for...in和Object.keys()中被枚举。

var person = {}; Object.defineProperty(person, "a", { value : 1, enumerable:true }); Object.defineProperty(person, "b", { value : 2, enumerable:false }); Object.defineProperty(person, "c", { value : 3 }); // enumerable defaults to false
person.d = 4; // 若是使用直接赋值的方式建立对象的属性,则这个属性的enumerable默认为true

for (var i in person) { console.log(i); }  // 'a' 和 'd' 
 Object.keys(person); // ["a", "d"]

访问器属性

访问器属性包含四个属性描述符:

[[Configurable]] : 表示可否经过delete删除属性从而从新定义属性,可否修改属性特性,可否把属性修改成数据属性。直接在对象上定义的属性,默认为true。

[[Enumerable]] : 表示可否经过for-in 循环访问属性。直接在对象上定义的属性,默认为true。

[[Get]] : 读取属性时调用的函数,默认为undefined。

[[Set]] : 写入属性时调用的函数,默认为undefined。

var person = {
    name: 'Nicy',
    _age: 21,
    year: 1997,
    _year: 1997,
    sayName: function() {
        console.log(this.name);
    }
}

Object.defineProperty(person, "age", {
    get: function() {
        return this._age;
    },
    set: function(value) {
        this._age = value;
                // ...
    }
})

用Object.defineProperty()定义的访问器属性,其configurable和enumerable默认为false。

数据属性与访问器属性的相互转换

Object.getOwnPropertyDescriptor 读取属性的特性

使用Object.getOwnPropertyDescriptor能够获取到属性的描述符:

Object.getOwnPropertyDescriptor(obj, prop)

  obj:属性所在的对象;

  prop:要访问的属性名。

数据属性 -> 访问器属性

属性的特性只能是访问器描述符和数据描述符中的一种,给已有的数据属性加get或set转换为访问器属性时,其属性的value、writable就会被废弃。

以下代码,将对象原有的数据属性year转换为访问器属性:

*注:在访问器属性的get和set中,不可使用this访问属性自己,不然会无限递归而致使内存泄漏。

// 设置get和set其中任意一个便可转换为访问器属性
Object.defineProperty(person, "year", {
    get: function() {
//        return this,year;    // error
        return this._year;    
    },
    set: function(value) {
//             this.year = value;  // error
        this._year= value;
    }
})

var descriptor = Object.getOwnPropertyDescriptor(person, 'year');
console.log(descriptor);    // {get: ƒ, set: ƒ, enumerable: true, configurable: true}

在原有的数据属性year中,使用Object.defineProperty()为属性设置get 或 set,均可以将其转换为访问器属性。

访问器属性 -> 数据属性

将访问器属性转换为数据属性,只须要给现有访问器属性设置value或writable这两个属性描述符中的任意一个便可,其原有的get和set就会被废弃,从而转换为数据属性。

上面为person定义的访问器属性age,经过Object.defineProperty()只设置了get和set,因此configurable默认为false,不能够将其转换为数据属性。能够在访问器属性和数据属性间相互转化的属性其configurable特性值必须为true。

以下代码,咱们为person新定义一个访问器属性job,将其configurable设置为true ,并将其转换为数据属性:

Object.defineProperty(person, "job", {
    configurable: true,
    enumerable: true,
    get: function() {
        return this._job;
    },
    set: function(value) {
        this._job = value;
    }
})

// 设置value和writable其中任意一个便可转换为数据属性        
Object.defineProperty(person, "job", {
    value: 'worker',
    writable: true
})

var descriptor = Object.getOwnPropertyDescriptor(person, 'job');
console.log(descriptor);    // {value: "worker", writable: true, enumerable: true, configurable: true}

数据描述符value、writable 和访问器描述符get、set不能同时设置,不然会报错。

Object.defineProperties()

经过Object.defineProperties()能够一次性为对象定义多个属性。

var person = {};
Object.defineProperties(person, {
  name: {
    value: 'Nicy',
    writable: true
  },
  _age: {
    value: 21,
    enumerable: true,
    writable: true,
    configurable: true
  },
   age: {
    get: function() {
    return this._age;
    },
    set: function(value) {
    this._age = value;
    }
  }
});
相关文章
相关标签/搜索