深入理解js对象中数据属性和访问器属性

更新日期: 2019-02-12 阅读: 3.9k 标签: 对象

创建对象的方式有两种:第一种,通过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);
    }
}

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


Object.defineProperty() 

通过Object.defineProperty() 可以直接在对象上创建一个属性,也可以修改已有的属性。

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

  obj:属性所在的对象  

  prop:要访问的属性名

  descriptor:描述符对象

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

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

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


数据属性

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

[[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时,

  1、不可以通过delete去删除该属性从而重新定义属性;

  2、不可以转化为访问器属性;

  3、configurable和enumerable不可被修改;

  4、writable可单向修改为false,但不可以由false改为true;

  5、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;
    }
  }
});

本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!

链接: https://fly63.com/article/detial/1974

相关推荐

js获取object对象的长度

我们都知道必须是具体数据类型才有长度,所以size和length都无法测量object对象的长度,那么如何计算对象的长度,即获取对象属性的个数呢?

JavaScript数组、对象合并的多种方法实现

这篇文章讲解Js数组和对象的一些使用技巧,如何将不同的数组,对象合并/结合为1个的方法

js中关于for...in遍历对象属性的顺序问题

对象使用obj.length时,它得到的值是undefined的,所以只能通过for...in循环获取对象的属性,我们发现并没有按属性的顺序显示,而且顺序在各个浏览器下显示也不同。 这是为什么呢?

JavaScript 判断对象中是否有某属性

判断对象中是否有某属性的常见方式总结,不同的场景要使用不同的方式。一点( . )或者方括号( [ ] )、二in 运算符、三hasOwnProperty()。三种方式各有优缺点,不同的场景使用不同的方式,有时还需要结合使用

JS 的 Document对象

Document 对象是是window对象的一个属性,因此可以将document对象作为一个全局对象来访问。当浏览器载入 HTML 文档, 它就会成为 Document 对象。Document对象的 属性和方法

Js通过.或者[]访问对象属性的语法、性能等区别

在JavaScript中可以使用 . 或者 [ ] 来访问对象的属性,但是对象中方法只能通过 . 来获取;使用.运算符来存取对象的属性的值。或者使用[]作为一个关联数组来存取对象的属性。但是这两种方式有什么区别了?

history对象详解及单页面路由实现

history对象保存着用户的上网记录,从浏览器窗口打开的那一刻算起。出于安全的考虑,开发人员无法得知用户浏览过的URL。不过,借由用户访问过的页面列表,同样可以在不知道实际URL的情况下实现后退与前进

js对象 对属性调用.和[] 两种方式的区别

在 JS 对象中,调用属性一般有两种方法——点和中括号的方法。 标准格式是对象.属性(不带双引号),注意一点的是:js对象的属性,key标准是不用加引号的,加也可以,特别的情况必须加,如果key数字啊,表达式啊等等

javascript中document是什么?

javascript中document是window对象的属性,表示对Document对象的只读引用。Document对象是Window对象的一部分,可通过window.document属性对其进行访问。

Js中Blob是什么?

MDN给出的解释:Blob 对象表示一个不可变、原始数据的类文件对象;创建一个blob只有两种方式1、通过new Blob();使用blob.slice切割,创建一个新的blob对象;读取blob唯一方式,使用fileReader

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!