JavaScript中对象的属性

原文:http://www.2ality.com/2012/10/javascript-properties.html

JavaScript中有三种不同类型的属性:命名数据属性(named data properties),命名访问器属性(named accessor properties)以及内部属性(internal properties).

命名数据属性

这种属性就是我们通常所用的"普通"属性,它用来将一个字符串名称映射到某个值上.比如,下面的对象obj有一个名为字符串"prop"的数据属性,该属性的值为数字123.

var obj = {    prop: 123
};

你可以获取(读取)到一个属性的值:

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

你还可以设置(写入)一个属性的值:

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

命名访问器属性

另外,还可以借助函数来获取或设置一个属性的值.这些函数称之为访问器函数(accessor function).控制属性读取的访问器函数称之为getter.控制属性写入的访问器函数称之为setter.

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

让我们操作一下obj的属性:

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

内部属性

有一些属性仅仅是为规范所用的,称之为内部属性,因为它们无法通过JavaScript直接访问到,但是它们的确存在,并且影响着程序的表现.内部属性的名称比较特殊,它们都被两个中括号包围着.下面有两个例子:

  • 内部属性[[Prototype]]指向了所属对象的原型.该属性的值可以通过Object.getPrototypeOf()函数读取到.该属性的值只能在创建一个新对象的时候通过Object.create()或者__proto__来设置 [1].
  • 内部属性[[Extensible]]决定了是否能给所属对象添加新的属性.该属性的值可以通过Object.isExtensible()读取到.还可以通过Object.preventExtensions()将该属性的值设置为false.一旦设置为false,就无法再设置回true了.

属性特性

一个属性的所有状态,包括它的数据和元数据,都存储在该属性的特性(attributes)中.属性拥有自己的特性,就像对象拥有自己的属性一样.特性的名称经常写成类似内部属性的形式(双中括号).

下面是命名数据属性拥有的特性:

  • [[Value]] 存储着属性的值,也就是属性的数据.
  • [[Writable]] 存储着一个布尔值,表明该属性的值是否可以改变.

下面是命名访问器属性拥有的特性:

  • [[Get]] 存储着getter,也就是在读取这个属性时调用的函数.该函数返回的值也就是这个属性的值.
  • [[Set]] 存储着setter,也就是在为这个属性赋值时调用的函数.该函数在调用时会被传入一个参数,参数的值为所赋的那个新值.

下面是两种类型的属性都有的特性:

  • [[Enumerable]] 存储着一个布尔值.可以让一个属性不能被枚举,在某些操作下隐藏掉自己(下面会有详细讲解).
  • [[Configurable]] 存储着一个布尔值.如果为false,则你不能删除这个属性,不能改变这个属性的大部分特性(除了[[Value]]),不能将一个数据属性重定义成访问器属性,或者反之.换句话说就是:[[Configurable]]控制了一个属性的元数据的可写性.

默认值

如果你不明确的指定某个特性的值,则它们会被赋一个默认值:

特性名称 默认值
[[Value]] undefined
[[Get]] undefined
[[Set]] undefined
[[Writable]] false
[[Enumerable]] false
[[Configurable]] false

这些默认值对于属性描述符尤其重要.

属性描述符

属性描述符(property descriptor)可以将一个属性的所有特性编码成一个对象并返回.该对象的每个属性都对应着所属属性的一个特性.例如,下面是一个值为123的只读属性的属性描述符:

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

你也可以使用一个访问器属性来实现上面这个拥有只读特性的数据属性,其属性描述符如下:

{
    get: function () { return 123 },
    //没有set,也就是只读
    enumerable: true,
    configurable: false
}

使用属性描述符的函数

在使用下面的函数时会用到属性描述符:

  • Object.defineProperty(obj, propName, propDesc)
    创建或改变对象obj的propName属性,propName属性的特性通过属性描述符propDesc给出.返回修改后的obj对象.例如:

    var obj = Object.defineProperty({}, "foo", {    value: 123,    enumerable: true
        // writable和configurable为默认值
    });
  • Object.defineProperties(obj, propDescObj)
    Object.defineProperty()的批处理版本.对象propDescObj的每个属性都指定了要给原对象obj添加或修改的一个属性和对应的属性描述符.例如:

    var obj = Object.definePropertys({}, {    foo: { value: 123, enumerable: true },    bar: { value: "abc", enumerable: true }});
  • Object.create(proto, propDescObj?)
    首先,创建一个原型为proto的对象.然后,如果提供了可选参数propDescObj,则会按照Object.defineProperties添加属性的方式给这个新对象添加属性.最后,返回操作后的新对象.例如,下面的代码创建的对象和上面的Object.definePropertys例子创建的对象完全一样:

    var obj = Object.create(Object.prototype, {    foo: { value: 123, enumerable: true },    bar: { value: "abc", enumerable: true }});
  • Object.getOwnPropertyDescriptor(obj, propName)
    返回对象obj的名为propName的自身属性(非继承来的)的属性描述符.如果没有这个自身属性,则返回undefined.

    > Object.getOwnPropertyDescriptor(Object.prototype, "toString"){ value: [Function: toString],  writable: true,  enumerable: false,  configurable: true }
    
    > Object.getOwnPropertyDescriptor({}, "toString")undefined

可枚举性

本节会解释什么操作会受到属性的可枚举性的影响,什么操作不会.我们首先假设已经定义了如下这样的对象proto和obj:

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

需要注意的是,所有对象(包括上面的proto)通常来说都至少有一个原型Object.prototype [2]:

> Object.getPrototypeOf({}) === Object.prototypetrue

我们常用的内置方法比如toString和hasOwnPropertyare等实际上都是定义在Object.prototype身上的.

受可枚举性影响的操作

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

for-in循环会遍历到一个对象的所有可枚举属性的名称,包括继承来的属性:

> for (var x in obj) console.log(x);   //没有遍历到Object.prototype上不可枚举的属性qux
baz
foo

Object.keys()返回一个对象的所有可枚举的自身属(非继承的)的名称组成的数组:

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

如果你想获取到所有的自身属性,则应该使用Object.getOwnPropertyNames().

不受可枚举性影响的操作

除了上面的两个操作,其他的操作都会忽略掉属性的可枚举性.一些读取操作会使用到继承来的属性:

> "toString" in objtrue
> 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 = valueobj["propName"] = value

delete obj.propNamedelete obj["propName"]

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

最佳实践

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

> 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[3].

如果你把对象当成是字符串到值的Map来使用的话,则你应该只操作自身属性且要忽略掉可枚举性.不过这种情况下还有很多其他陷阱需要考虑[4].

结论

在本文中,我们对属性的性质(称之为特性)进行了研究.需要注意的是,实际上JavaScript引擎并不是必须得通过特性来组织一个属性,它们主要是作为ECMAScript规范中定义的一个抽象操作.但有时候这些特性也会明确的出现在语言代码中,比如在属性描述符中.

参考

  1. JavaScript: __proto__
  2. What object is not an instance of Object?
  3. Iterating over arrays and objects in JavaScript
  4. The pitfalls of using objects as maps in JavaScript
时间: 2024-10-16 15:07:27

JavaScript中对象的属性的相关文章

记录,javascript中对象的属性名是字符串,却可以不用引号

问题描述:今日看书,里面介绍js的对象的属性名是包括空字符串在内的所以字符串 问题来了,我们平时定义的对象如下,是没有引号""or’'的 var someone  = {    first_name : “Jeo”,    second_name : “Mike”}; 所以我写了以下程序测试, var stooge = {    “first_name” : “Jeo”,    “second_name” : “Mike”};var copy = {    first_name : “

javascript中对象的属性的特性

enumberable: 表示是否能通过for-in循环返回属性.默认为true writable: 是否可以修改属性, 默认为true value: 包含这个属性的数据值.读取属性值时3,从这个属性读,写入属性时,把新值保存到这个位置.默认值为undefine. getter: 在读取属性时,调用的函数 setter: 在写入属性时调用的函数 特别注意:一旦调用了Object.defineProperty方法之后,那些未定义的特性值除了configurable为false之外,其他都为unde

JavaScript中对象属性的添加和删除

<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> <script type="text/javascript"> /* * 用.为对象添加属性 用关键字delete删除属性 用[]添加属性 和.的不同 r.name==r["name"]; r.

javascript中常用坐标属性offset、scroll、client

原文:javascript中常用坐标属性offset.scroll.client 今天在学习js的时候觉得这个问题比较容易搞混,所以自己画了一个简单的图,并且用js控制台里面输出测试了下,便于理解. 1.在文档(document)对象里面用: scrollWidth/Height:获取对象的滚动宽度(滚动条可以滚动的宽度,相当于整个页面的总宽度的样子--网页正文全宽) scrollLeft/Top:设置或获取位于对象左边界和窗口中目前可见内容的最左端之间的距离(页面利用滚动条滚动到右边时,隐藏在

私人定制javascript中对象小知识点(Only For Me)

废话不多讲,先上笑话,然后再,.看懂这个的说明你的节操已经不再了. 晚饭后去理发店理发...割了吧...老板问我怎么剪,我悠悠的来一句往帅了剪...高潮往往令人想不到....旁边一在焗油烫头发的大妈说到 别这样为难老板,人家赚点钱不容易...首先如果你是高手那么请出门右转,如果你是菜鸟那么恭喜你,go on吧 全局对象在javascript程序中任何位置,都可以拈来就用的这种东西,是全局对象的属性.那么属性所在的对象也就是全局对象了.当javascript解释器启动时(或者任何Web浏览器加载新

javascript: object对象按属性排序的方法

javascript中在使用ajax和服务端交互时会遇到这样的场景,服务端返回json格式数据,前端展示时需要对数据进行某种排序,比如升序.降序. 可以利用 sort() 自定义排序规则来实现,例如返回的一段json数据,按照年龄升序排列. 1 var res = { 2 success: true, 3 result: [ 4 { 5 id: 1, 6 name: 'Jack', 7 age: 21 8 }, 9 { 10 id: 2, 11 name: 'Tom', 12 age: 23 1

JavaScript中Number常用属性和方法

title: JavaScript中Number常用属性和方法 toc: false date: 2018-10-13 12:31:42 Number.MAX_VALUE--1.7976931348623157e+308,可表示的最大数 Number.MIN_VALUE--5e-324,可表示的最小数 toExponential(x)--把对象的值转换为指数计数法 toFixed(x)--把数字转换为字符串,x为小数点后位数 toPrecision(x)--把数字格式化为指定的长度 toStri

JavaScript中对象类型的转换小结

对象到字符串和对象到数字类型的转换涉及到两个重要的方法,最终的转换结果会受到这两个方法返回结果的影响,这两个方法就是toString和valueOf.所有的对象都会从Object对象中继承到这两个方法.toString方法 用于返回对象的字符串表示(但是其实也可以不返回字符串).对于默认从Object继承而来的toString方法并不会返回太多有意义的内容.而valueOf方法目的是返回一个可以表示对象的原始类型值,但是由于对象的复杂性,大多数情况下根本不可能用一个原始类型值来表示,所以默认的v

[ jquery 方法 text([val|fn]) ] 此方法操作匹配的元素或元素集中的内容,相当于javascript中的innerText属性

取得所有匹配元素的内容:结果是由所有匹配元素包含的文本内容组合起来的文本,这个方法对HTML和XML文档都有效,相当于javascript中的innerText属性,可读可写,但是无法解析HTML标签: 如果传入的是callback,此函数返回一个字符串并且参数解析如下: 1.index为元素在集合中的索引位置 2.text为原先的text值. 实例: <!DOCTYPE html> <html lang='zh-cn'> <head> <title>Ins