数据属性

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

  • [[Configurable]]:表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。
  • [[Enumerable]]:表示能否通过 for-in 循环返回属性。
  • [[Writable]]:表示能否修改属性的值。
  • [[Value]]:包含这个属性的值。读取属性值的时候,从这个位置读;写入属性值的时候,把新值保存在这个位置。这个特性的默认值为 undefined。

数据属性可以直接定义。对于直接在对象上定义的属性,它们的 [[Configurable]]、[[Enumerable]] 以及 [[Writable]] 特性都被设置为 true,而 [[Value]] 特性被设置为指定的值。

例如定义如下一个对象:

var person = {
  name: 'hanzichi'
};

这里创建了一个名为 name 的属性,为它指定的值是 'hanzichi'。也就是说,[[Value]] 特性将被设置为 'hanzichi',而对这个值的任何修改都将反映在这个位置。

要修改默认属性的特性,必须使用 ES5 的 Object.defineProperty() 方法。这个方法接收三个参数:属性所在的对象、属性的名字和一个描述符对象。其中,描述符对象的属性必须是:configurable、enumerable、writable 和 value。例如:

var person = {
  name: 'hanzichi'
};

Object.defineProperty(person, 'name', {
  value: 'zichi',
});

person.name;
// => zichi

利用 Object.defineProperty 也可以创建对象的新的属性。

var person = {};
Object.defineProperty(person, 'name', {
  value: 'hanzichi'
});

如上,person 对象的 name 属性,其 [[Configurable]]、[[Enumerable]] 以及 [[Writable]] 特性默认为 false。

访问器属性

访问器属性不包含数据值(没有 [[Value]] 特性),它们包含一对 getter 和 setter 函数(这两个函数都不是必须的)。在读取访问器属性时,会调用 getter 函数,这个函数负责返回有效的值;在写入访问器属性时,会调用 setter 并传入新值,这个函数负责决定如何处理数据。访问器属性有如下 4 个特性。

  • [[Configurable]]:表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为数据属性。
  • [[Enumerable]]:表示能否通过 for-in 循环返回属性。
  • [[Get]]:在读取属性时调用的函数。默认值为 undefined。
  • [[Set]]:在写入属性时调用的函数。默认值为 undefined。

访器属性不能直接定义,必须使用 Object.defineProperty() 来定义。

var book = {
  _year: 2004,
  edition: 1
};

Object.defineProperty(book, 'year', {
  get: function() {
    return this._year;
  },
  set: function(newValue) {
    if (newValue > 2004) {
      this._year = newValue;
      this.edition += newValue - 2004;
    }
  }
});

book.year = 2005;
alert(book.edition);  // 2
alert(book.year); // 2005

以上代码创建了一个 book 对象,并给它定义了两个默认的属性:_year 和 edition。_year 前面的下划线是一种常用的记号,用于表示只能通过对象方法访问的属性(虽然理论上是可以直接访问的)。而访问器属性 year 则包含一个 getter 函数和一个 setter 函数。getter 函数返回 _year 的值,setter 函数通过计算来确定正确的版本。因此,把 year 属性修改为 2005 会导致 _year 变成 2005,而 edition 变为 2。这是使用访问器属性的常见方式,即设置一个属性的值会导致其他属性发生变化。

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

由此可以联想到数据对象与 DOM 对象的 "双向绑定"。

Object.defineProperty(user, 'name', {
  get: function () {
    return document.getElementById('foo').value;
  },
  set: function (newValue) {
    document.getElementById('foo').value = newValue;
  },
  configurable: true
});

上面代码使用存取函数,将 DOM 对象 foo 与数据对象 user 的 name 属性,实现了绑定。两者之中只要有一个对象发生变化,就能在另一个对象上实时反映出来。

[[Configurable]]

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

var person = {};
Object.defineProperty(person, 'name', {
  value: 'hanzichi',
  configurable: false // 其实默认就是 false
});

delete person.name;
alert(person.name); // hanzichi

需要注意的是,当使用 var 命令声明变量时,变量的 configurable 为 false。

var a1 = 1;

Object.getOwnPropertyDescriptor(this,'a1')
// Object {
//  value: 1,
//  writable: true,
//  enumerable: true,
//  configurable: false
// }

而不使用 var 命令声明变量时(或者使用属性赋值的方式声明变量),变量的可配置性为 true。

a2 = 1;

Object.getOwnPropertyDescriptor(this,'a2')
// Object {
//  value: 1,
//  writable: true,
//  enumerable: true,
//  configurable: true
// }

// 或者写成

window.a3 = 1;

Object.getOwnPropertyDescriptor(window, 'a3')
// Object {
//  value: 1,
//  writable: true,
//  enumerable: true,
//  configurable: true
// }

[[Configurable]] 特性还有个作用,一旦把属性定义为不可配置的,就不能再把它变为可配置了。也就是说,当 configurable 为 false 的时候,value、writable、enumerable 和 configurable 都不能被修改了。

var o = Object.defineProperty({}, 'p', {
  value: 1,
  writable: false,
  enumerable: false,
  configurable: false
});

Object.defineProperty(o,'p', {value: 2})
// TypeError: Cannot redefine property: p

Object.defineProperty(o,'p', {writable: true})
// TypeError: Cannot redefine property: p

Object.defineProperty(o,'p', {enumerable: true})
// TypeError: Cannot redefine property: p

Object.defineProperties(o,'p',{configurable: true})
// TypeError: Cannot redefine property: p

需要注意的是,writable 只有在从 false 改为 true 会报错,从 true 改为 false 则是允许的。

var o = Object.defineProperty({}, 'p', {
  writable: true,
  configurable: false
});

Object.defineProperty(o,'p', {writable: false})
// 修改成功

至于 value,只要 writable 和 configurable 有一个为 true,就允许改动。

var o1 = Object.defineProperty({}, 'p', {
  value: 1,
  writable: true,
  configurable: false
});

Object.defineProperty(o1,'p', {value: 2})
// 修改成功

var o2 = Object.defineProperty({}, 'p', {
  value: 1,
  writable: false,
  configurable: true
});

Object.defineProperty(o2,'p', {value: 2})
// 修改成功

[[Enumerable]]

enumerable 存放一个布尔值,表示该属性是否可枚举,如果设为 false,会使得某些操作(比如 for-in 循环、Object.keys() 以及 JSON.stringify 会跳过该属性)

var obj = {
  a: 10,
  b: 20
};

Object.defineProperty(obj, 'c', {
  value: 30,
  enumerable: false
});

for (var key in obj)
  console.log(key, obj[key]);

// a 10
// b 20

[[Writable]]

[[Writable]] 特性只存在于数据属性中,一旦被设置为 false,那么该属性值就不能被修改(只读)。如果尝试为它指定新值,则在非严格模式下,会被忽略,严格模式下报错。

var person = {};
Object.defineProperty(person, 'name', {
  value: 'hanzichi',
  writable: false // 其实默认就是 false
});

person.name = 'zichi';
alert(person.name); // hanzichi

不过如果是用 Object.defineProperty 对属性重新赋值,就会直接报错。

var person = {};
Object.defineProperty(person, 'name', {
  value: 'hanzichi',
  writable: false // 其实默认就是 false
});

Object.defineProperty(person, 'name', {
  value: 'zichi',
});

// Uncaught TypeError: Cannot redefine property: name

当然前面也提到了,如果 writable 为 false,但是 configurable 为 true,还是可以对属性重新赋值的。

其他

我们可以用 Object.defineProperties() 方法同时定义多个属性。

var person = {};
Object.defineProperties(person, {
  name: {
    value: 'hanzichi',
    writable: true
  },
  age: {
    value: 30,
    enumerable: true
  }
});

我们可以用 Object.getOwnPropertyDescriptor() 方法取得给定属性的描述符。返回是一个对象,如果是数据属性,这个返回对象的属性有 configurable、enumerable、writable 以及 value;如果是访问器属性,则这个对象的属性有 configurable、enumerable、get 以及 set。

var person = {};
Object.defineProperty(person, 'name', {
  value: 'hanzichi',
  configurable: false // 其实默认就是 false
});

var descriptor = Object.getOwnPropertyDescriptor(person, 'name');
console.log(descriptor);
// Object {value: "hanzichi", writable: false, enumerable: false, configurable: false}

var book = {
  _year: 2004,
  edition: 1
};

Object.defineProperty(book, 'year', {
  get: function() {
    return this._year;
  },
  set: function(newValue) {
    if (newValue > 2004) {
      this._year = newValue;
      this.edition += newValue - 2004;
    }
  }
});

Object.getOwnPropertyDescriptor(book, 'year');
// 可以自行打印看看,4 个 key

一个属性只能是数据属性或者访问器属性,所以均拥有 [[Configurable]] 和 [[Enumerable]] 两个特性,但是对于其他两个属性,则分别拥有,所以不可能同时有 [[Value]] 和 [[Set]],[[Writable]] 和 [[Set]] 等(如果同时定义则会报错)。

Read more

JavaScript 属性类型(数据属性和访问器属性)的更多相关文章

  1. ECMAScript中有两种属性:数据属性和访问器属性。

    ECMA-262定义这些特性是为了实现JavaScript引擎用的,因此在JavaScript中不能直接访问它们.为了表示特性是内部值,该规范把它们放在了两对儿方括号中,例如 [[Enumerable ...

  2. JavaScript中的数据属性和访问器属性

    在学习JavaScript原型(prototype)和原型链(prototype chain)知识的时候,发现数据属性和访问器属性的重要性,通过不断的查找相关知识,浅显理解如下,若有差错,希望不吝赐教 ...

  3. JavaScript对象的两类属性(数据属性与访问器属性)

    对JavaScript来说,属性并非只是简单的名称和值,JavaScript用一组特征(attribute)来描述属性 (property). 第一类属性数据属性具有四个特征. value:就是属性的 ...

  4. JavaScript 数据属性和访问器属性

    在JavaScript中对象被定义为"无序属性的集合,其属性可以包含基本值.对象或函数."通俗点讲,我们可以把对象理解为一组一组的名值对,其中值可以是数据或函数. 创建自定义对象通 ...

  5. javascript对象属性——数据属性和访问器属性

    ECMA-262第五版在定义时,描述了属性property的各种特征,定义这些特性是为了实现javascript引擎用的,为了表示该特性是内部值,规范把它们放在了两对儿方括号中,例如[[Enumera ...

  6. js中属性类型:数据属性与访问器属性

    js中属性类型分为两种:数据属性和访问器属性 在js中,对象都是由名值对构成的,名:就是我们所说的属性名,值就是属性对应的值(基本值.对象.方法). ECMA-262第5版定义了只有内部才用的特性,描 ...

  7. JavaScript数据属性与访问器属性

    ES5中对象的属性可以分为‘数据属性’和‘访问器属性’两种. 数据属性一般用于存储数据数值,访问器属性对应的是set/get操作,不能直接存储数据值. 数据属性特性:value.writable.en ...

  8. 浅谈Javascript数据属性与访问器属性

    ES5中对象的属性可以分为‘数据属性’和‘访问器属性’两种. 数据属性一般用于存储数据数值,访问器属性对应的是set/get操作,不能直接存储数据值. 数据属性特性:value.writable.en ...

  9. Js中的数据属性和访问器属性

    Js中的数据属性和访问器属性 在javaScript中,对象的属性分为两种类型:数据属性和访问器属性. 一.数据属性 1.数据属性:它包含的是一个数据值的位置,在这可以对数据值进行读写. 2.数据属性 ...

随机推荐

  1. scikit-learn一般实例之四:使用管道和GridSearchCV选择降维

    本例构建一个管道来进行降维和预测的工作:先降维,接着通过支持向量分类器进行预测.本例将演示与在网格搜索过程进行单变量特征选择相比,怎样使用GrideSearchCV和管道来优化单一的CV跑无监督的PC ...

  2. Marshal.Copy将指针拷贝给数组

    lpStatuss是一个UNITSTATUS*的指针类型实例,并包含SensorDust字段 //定义一个数组类型 byte[] SensorDust = new byte[30] //将指针类型拷贝 ...

  3. SSH中Action的单例与多例

    Structs2中的Bean默认的是单例,在整个程序运行期间,每个Bean只有一个实例,只要程序在运行,这个实例就一直存在. 对于Action来说,单例就容易出问题.如果客户端每次提交的参数都是一样的 ...

  4. spider RPC开发指南

    协议与兼容性 spider使用java语言开发,使用Spring作为IoC容器,采用TCP/IP协议,在此基础上,结合SaaS系统模式的特性进行针对性和重点设计,以更加灵活和高效的满足多租户系统.高可 ...

  5. 以ZeroMQ谈消息中间件的设计【译文】

    本文主要是探究学习比较流行的一款消息层是如何设计与实现的 ØMQ是一种消息传递系统,或者乐意的话可以称它为"面向消息的中间件".它在金融服务,游戏开发,嵌入式系统,学术研究和航空航 ...

  6. mysql 插入数据失败防止自增长主键增长的方法

    mysql设置了自增长主键ID,插入失败的那个自增长ID也加一的,比如失败5个,下一个成功的不是在原来最后成功数据加1,而是直接变成加6了,失败次数一次就自动增长1了,能不能让失败的不增长的? 或者说 ...

  7. 【原】低版本MyEclipse整合高版本Tomcat

    [使用工具] 1.MyEclipse_6.0.1GA_E3.3.1_FullStackInstaller 2.Tomcat 7.0 [问题描述] 直接在MyEclipse中整合,因为这个版本的MyEc ...

  8. [备查]使用 SPQuery 查询 "Person or Group" 字段

    原文地址:http://www.stum.de/2008/02/06/querying-the-person-or-group-field-using-spquery/ Querying the “P ...

  9. 阶段一:通过网络请求,获得并解析JSON数据(天气应用)

    “阶段一”是指我第一次系统地学习Android开发.这主要是对我的学习过程作个记录. 在上一篇阶段一:解析JSON中提到,最近在写一个很简单的天气预报应用.即使功能很简单,但我还是想把它做成一个相对完 ...

  10. Android—IMEI

    TelephonyManager telephonyManager= (TelephonyManager) getSystemService(TELEPHONY_SERVICE); // Return ...