属性类型

ECMA-262第5版在定义只有内部才用的特性(attribute)时,描述了属性(property)的各种特征。ECMA-262定义这些特性是为了实现JavaScript引擎用的,因此在JavaScript中不能直接访问它们。为了表示特性是内部值,该规范把它们放在两对儿方括号中,例如[[Enumerable]]
ECMAScript中有两种属性:数据属性访问器属性

数据属性

数据属性包含一个数据值的位置。在这个位置可以读取和写入值。数据属性有4个描述其行为的特性。
**

[[Configurable]]

  1. 表示能否通过delete删除属性从而重新定义属性(可配置的)
  2. 能否修改属性的特性
  3. 能否把属性修改为访问器属性。
  4. 默认值为true

[[Enumerable]]

  1. 表示能否通过for-in循环遍历属性(可枚举的)
  2. 默认值为true

[[Writable]]

  1. 表示能否修改属性的值。(可写的)
  2. 默认值为true

[[Value]]

  1. 包含这个属性的数据值。
  2. 读取属性值的时候,从这个位置读;
  3. 写入属性值的时候,把新值保存在这个位置。
  4. 默认值为undefined

总结一下就是:

  1. 1.configurable属性是配置属性,决定了能不能删除(delete)属性,能不能修改属性的特性。
  2. 所以这个属性最好保持为true,变为false的话还无法将其改回true
  3. 2.enumberablewritable从字面意思就可以看出作用,一个枚举一个修改。
  4. 3.value就是属性的值

Object.defineProperty()方法

要修改属性的默认的特性,必须使用ES5的这个方法。这个方法接收三个参数:对象,属性名和一个描述符对象
其中描述符对象的属性就是以上介绍的四个数据属性。例如:

  1. var person = {};
  2. Object.defineProperty(person,"name",{
  3. writable:false,
  4. value:"Nicholas"
  5. });
  6. console.log(person.name); // "Nicholas"
  7. person.name = "Greg";
  8. console.log(person.name); // "Nicholas"

分析:由于我们将person对象的name属性的writable属性设为false,所以表示这个name属性无法修改,后面尝试修改的话就会失败的。

类似的规则也适用与不可配制的属性。例如:

  1. var person = {};
  2. Object.defineProperty(person,"name",{
  3. configurable:false,
  4. value:"Nicholas"
  5. });
  6. console.log(person.name); // "Nicholas"
  7. delete person.name;
  8. console.log(person.name); // "Nicholas"

分析:把configurable设置为false,表示不能从对象中删除属性。如果对这个属性调用delete,则在非严格模式下什么也不会发生,而在严格模式下会导致错误。

而且,一旦把属性定义为不可配置的(false),就不能再把它变回可配置了。此时,再调用Object.defineProperty()方法修改除writable之外的特性,都会导致错误:

  1. var person = {};
  2. Object.defineProperty(person,"name",{
  3. configurable:false,
  4. value:"Nicholas"
  5. });
  6. // 抛出错误 "Uncaught TypeError: Cannot redefine property: name"
  7. Object.defineProperty(person,"name",{
  8. configurable:true,
  9. value:"Nicholas"
  10. });

调用Object.defineProperty()方法时,如果不显式指定,configurableenumerablewritable特性的默认值都是false

访问器属性

访问器属性不包含数据值;它们包含一对儿gettersetter函数(不过,这两个函数都不是必需的)。

  • 在读取访问器属时,会调用getter函数,这个函数负责返回有效的值;
  • 在写入访问器属性时,会调用setter函数并传入新值,这个函数负责决定如何处理数据。

访问器的四个属性
image.png
访问器属性不能直接定义,必须使用Object.defineProperty()来定义。

  1. var book = {
  2. _year:2004,
  3. edition:1
  4. };
  5. Object.defineProperty(book,"year",{
  6. get:function () {
  7. return this._year;
  8. },
  9. set:function (newValue) {
  10. if ((newValue > 2004)) {
  11. this._year = newValue;
  12. this.edition += newValue - 2004;
  13. }
  14. }
  15. })
  16. book.year = 2005;
  17. alert(book.edition); // 2

创建一个book对象,并给它定义两个默认的属性:_yearedition_year前面的下划线是一种常用的记号,用于表示只能通过对象方法访问的属性。而访问器属性year则包含一个getter函数和一个setter函数

get和set方法

不一定非要同时指定getter和setter。只指定getter意味着属性是不能写,尝试写入属性会被忽略。在严格模式下,尝试写入只指定了getter函数的的属性会抛出错误。类似地,没有指定setter函数的属性也不能读,否则在非严格模式下会返回undefined,在严格模式下会抛出错误。

  1. // 下划线表示只能通过对象方法访问的属性
  2. var obj = {
  3. _x:"obj._x",
  4. _y:"obj._y",
  5. _z:"obj._z"
  6. };
  7. //
  8. Object.defineProperty(obj,"x",{ // x属性,只读不能写
  9. get:function () {
  10. return this._x;
  11. }
  12. });
  13. console.log(obj.x); // "obj._x" 可以读取,调用obj.x实际上调用了obj._x的getter函数
  14. obj.x = "Rewrite x"; // 尝试修改x属性
  15. console.log(obj.x); // "obj._x" 写入失败
  16. //
  17. Object.defineProperty(obj,"y",{ // y属性,只写不能读
  18. set:function (newValue) {
  19. this._y = newValue;
  20. }
  21. });
  22. console.log(obj.y); // "undefined" 读取失败
  23. obj.y = "Rewrite obj.y"; // 尝试修改属性
  24. console.log(obj._y); // "Rewrite obj.y" 可以写入
  25. //
  26. Object.defineProperty(obj,"z",{ // z属性可读可写
  27. get:function () {
  28. return this._z;
  29. },
  30. set:function (newValue) {
  31. this._z = newValue;
  32. }
  33. });
  34. console.log(obj.z); // "obj._z"
  35. obj.z = "Rewrite obj.z"; // 修改z属性
  36. console.log(obj._z); // "Rewrite obj._z"

定义多个属性

Object.defineProperties()
此方法用来定义多个属性。这个方法接收两个对象参数:第一个参数是要添加和修改其属性的对象,第二个对象的属性与第一个对象中要添加或修改的属性一一对应。例如:

  1. var book = {};
  2. Object.defineProperties(book,
  3. {
  4. _year:{
  5. writable:true,
  6. value:2004
  7. },
  8. edition:{
  9. writable:true,
  10. value:1
  11. },
  12. year:{
  13. get:function () {
  14. return this._year;
  15. },
  16. set:function (newValue) {
  17. if (newValue > 2004) {
  18. this._year = newValue;
  19. this.edition += newValue - 2004;
  20. }
  21. }
  22. },
  23. });

读取属性的特性

Object.getOwnPropertyDescriptor()
使用ES5的Object.getOwnPropertyDescriptor()方法,可以取得给定属性的描述符,这个方法接收两个参数:属性所在的对象和要读取其描述符的属性名称。返回值是一个对象,
如果是访问器属性,这个对象的属性有configurableenumerablegetset;
如果是数据属性,这个对象的属性有configurableenumerablewritablevalue

  1. var book = {};
  2. Object.defineProperties(book,{
  3. _year:{
  4. value:2004
  5. },
  6. edition:{
  7. value:1
  8. },
  9. year:{
  10. get:function () {
  11. return this._year;
  12. },
  13. set:function (newValue) {
  14. if (newValue > 2004) {
  15. this._year = newValue;
  16. this.edition += newValue - 2004;
  17. }
  18. }
  19. }
  20. });
  21. var descriptor = Object.getOwnPropertyDescriptor(book,"_year");
  22. alert(descriptor.value); // 2004
  23. alert(descriptor.configurable); // false
  24. alert(typeof descriptor.get); // "undefined"
  25. var descriptor = Object.getOwnPropertyDescriptor(book,"year");
  26. alert(descriptor.value); // "undefined"
  27. alert(descriptor.enumerable); // false
  28. alert(descriptor.get); // "function"