深入JS对象属性

更新日期: 2019-08-10 阅读: 2.6k 标签: 对象

属性决定JS中对象的状态,本文章主要分析这些属性是如何工作的。


JS几种不同的属性

JS有三种不同的属性:数据属性,访问器属性和内部属性。

1.1 数据属性(properties)

对象的普通属性将字符串名称映射到值。例如,下面对象obj有一个数据属性,名称为 prop,对应的值为 123:

var obj = {
    prop: 123
};

可以用以下方式读取属性的值:

console.log(obj.prop); // 123
console.log(obj["prop"]); // 123

当然也可以用以下方式来设置属性的值:

obj.prop = "abc";
obj["prop"] = "abc";

1.2 访问器属性

另外,可以通过函数处理获取和设置属性值。 这些函数称为访问器函数。 处理获取的函数称为getter。 处理设置的函数称为setter:

var obj = {
  get prop () {
    return 'Getter';
  },
  set prop (value) {
    console.log('Setter: ' + value);
  }
}

访问 obj 属性:

> obj.prop
 'Getter'
> obj.prop = 123;
  Setter: 123

1.3 内部属性

一些属性只是用于规范,这些属于“内部”的内部,因为它们不能直接访问,但是它们确实影响对象的行为。内部属性有特殊的名称都写在两个方括号,如:

  • 内部属性[[Prototype]]指向对象的原型。它可以通过Object.getPrototypeOf()读取。它的值只能通过创建具有给定原型的新对象来设置,例如通过object.create()或__proto__ 。
  • 内部属性[[Extensible]]决定是否可以向对象添加属性。可以通过Object.isExtensible() 方法判断一个对象是否是可扩展的(是否可以在它上面添加新的属性)。可以通过Object.preventExtensions()方法让一个对象变的不可扩展,也就是永远不能再添加新的属性。


2. 属性特性(attribute)

属性的所有状态,包括数据和元数据,都存储在特性(attribute)中。它们是属性具有的字段,就像对象具有属性一样。特性(attribute)键通常用双括号编写:

以下特性是属于数据属性:

  • [[Value]]:该属性的属性值,默认为undefined。
  • [[Writable]]:是一个布尔值,表示属性值(value)是否可改变(即是否可写),默认为true。

以下特性是属于访问器属性:

[[Get]]:是一个函数,表示该属性的取值函数(getter),默认为undefined

[[Set]]:是一个函数,表示该属性的存值函数(setter),默认为undefined。

所有的属性都具有以下的特性:

[[Enumerable]]:是一个布尔值,表示该属性是否可遍历,默认为true。如果设为false,会使得某些操作(比如for...in循环、Object.keys())跳过该属性。

[[Configurable]] :是一个布尔值,表示可配置性,默认为true。如果设为false,将阻止某些操作改写该属性,比如无法删除该属性,也不得改变该属性的属性描述对象(value属性除外)。也就是说,configurable属性控制了属性描述对象的可写性。


3. 属性描述

JavaScript 提供了一个内部数据结构,用来描述对象的属性,控制它的行为,比如该属性是否可写、可遍历等等。这个内部数据结构称为“属性描述对象”(attributes object)。每个属性都有自己对应的属性描述对象,保存该属性的一些元信息。下面是值为123属性描述对象的一个例子。

{
    value: 123,
    writable: false,
    enumerable: true,
    configurable: false
}

咱们也可以通过访问器属性实现相同的目标,属性描述对象如下所示:

{
  get: function () { return 123 },
  enumerable: true,
  configurable: false
}

3.1 使用属性描述符的函数

下面的函数允许咱们使用属性描述符:

Object.defineProperty(obj, propName, propDesc):该方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。

obj:要在其上定义属性的对象。
prop:要定义或修改的属性的名称。
descriptor:将被定义或修改的属性描述符。

var obj = Object.defineProperty({}, "foo", {
  value: 123,
})

Object.defineProperties(obj, propDescObj): 该方法直接在一个对象上定义一个或多个新的属性或修改现有属性,并返回该对象。

obj: 将要被添加属性或修改属性的对象

props: 该对象的一个或多个键值对定义了将要为对象添加或修改的属性的具体配置

var obj = Object.defineProperties({}, {
  foo: { value: 123, enumerable: true },
  bar: { value: "abc", enumerable: true }
});

Object.create(proto, propDescObj?): 方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。

proto:新创建对象的原型对象。
propDescObj:可选。如果没有指定为 undefined,则是要添加到新创建对象的可枚举属性(即其自身定义的属性,而不是其原型链上的枚举属性)对象的属性描述符以及相应的属性名称。这些属性对应Object.defineProperties()的第二个参数。

var obj = Object.create(Object.prototype, {
  foo: { value: 123, enumerable: true },
  bar: { value: "abc", enumerable: true }
})

Object.getOwnPropertyDescriptor(obj, propName): 该方法返回指定对象上一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)

obj:需要查找的目标对象
prop:目标对象内属性名称

var o, d;

o = { get foo() { return 17; } };
d = Object.getOwnPropertyDescriptor(o, "foo");
// d {
//   configurable: true,
//   enumerable: true,
//   get: /*the getter function*/,
//   set: undefined
// }


4.可枚举性(Enumerable)

本节说明哪些操作受可枚举性影响,哪些操作不受可见性影响。 下面,假设有以下定义:

var proto = Object.defineProperties({}, {
  foo: { value: 1, enumerable: true },
  bar: { value: 2, enumerable: false }
})

var obj = Object.create(proto, {
  baz: { value: 1, enumerable: true },
  gux: { value: 2, enumerable: false}
})

请注意,对象(包括proto)通常至少具有原型Object.prototype:

> Object.getPrototypeOf({}) === Object.prototype
  true

Object.prototype是定义标准方法(如toString和hasOwnProperty)的地方。

4.1 受可枚举性影响的操作

可枚举性仅影响两个操作:for-in循环和Object.keys()。

for-in循环遍历所有可枚举属性的名称,包括继承的属性(请注意,Object.prototype的所有非可枚举属性都不会显示):

> for (var x in obj) console.log(x);
    baz
    foo

Object.keys() 返回所有自有(非继承)可枚举属性的名称:

> Object.keys(obj)
 [ 'baz' ]

如果需要所有属性的名称,则需要使用Object.getOwnPropertyNames()。

4.2 忽略可枚举性的操作

除了上达两个,其他操作都忽略了可枚举性,还有一些操作会考虑继承:

> "toString" in obj
true
> obj.toString
[Function: toString]

还有一些仅读取自有属性:

> Object.getOwnPropertyNames(obj)
[ 'baz', 'qux' ]

> obj.hasOwnProperty("qux")
true
> obj.hasOwnProperty("toString")
false

> Object.getOwnPropertyDescriptor(obj, "qux")
{ value: 2,
  writable: false,
  enumerable: false,
  configurable: false }
> Object.getOwnPropertyDescriptor(obj, "toString")
undefined

创建,删除和定义属性仅影响原型链中的第一个对象:

obj.propName = value
obj["propName"] = value

delete obj.propName
delete obj["propName"]

Object.defineProperty(obj, propName, desc)
Object.defineProperties(obj, descObj)


5. 最佳实践

一般规则是系统创建的属性是不可枚举的,而用户创建的属性是可枚举的:

> Object.keys([])
[]
> Object.getOwnPropertyNames([])
[ 'length' ]
> Object.keys(['a'])
[ '0' ]

这特别适用于原型对象中的方法:

> Object.keys(Object.prototype)
[]
> Object.getOwnPropertyNames(Object.prototype)
[ hasOwnProperty',
  'valueOf',
  'constructor',
  'toLocaleString',
  'isPrototypeOf',
  'propertyIsEnumerable',
  'toString' ]

因此,对于咱们的代码,应该忽略可枚举性。通常不应该向内置原型和对象添加属性,但如果这样做,咱们就应该使它们不可枚举以避免破坏内置代码。

正如咱们所看到的,非可枚举性主要受益于for-in并且确保使用它的遗留代码不会中断。 不可枚举的属性创建了一种错觉,即for-in仅迭代用户创建的对象自有的属性。 在咱们的代码中,如果可以,应该避免使用for-in。

原文:https://2ality.com/2012/08/instanceof-object.html

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

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

相关推荐

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

点击更多...

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