JavaScript中普通属性和排序属性详解

 更新时间:2023年09月18日 10:14:48   作者:LBruse  
JavaScript属性是对象中的特性,用于描述对象的状态,每个JavaScript对象都有一组属性,可以通过点号(.)或方括号([])访问和操作这些属性,本文将给大家讲讲JavaScript中你所不知道的普通属性和排序属性,需要的朋友可以参考下

普通属性

JavaScript中定义对象时,普通的以字符串为键定义的属性为普通属性,特点是在遍历对象属性时根据创建时的顺序排序

const obj = {
    'name': 'Bruse',
    'age': 16,
}
for (const key in obj) {
    console.log(key)
}

输出顺序为

name
age

排序属性

排序属性则是以数字为键定义的属性,特点是按照索引值的大小进行升序排序,优先级优于普通属性。

const obj = {
    'name': 'Bruse', // 普通属性
    'age': 16, // 普通属性
    2: 'obj2' // 排序属性
}
obj[1] = 'obj1' // 排序属性
obj[99] = 'obj99' // 排序属性
obj[10] = 'obj10' // 排序属性
for (const key in obj) {
    console.log(key)
}

输出

1
2
10
99
name
age

可以看到在使用for ... in 遍历obj的属性时,排序属性遍历顺序优先于普通属性,同时排序属性也是按照键值进行升序排序的,键值越小,遍历顺序越往前

字符串数字作为键

obj中新建一个键为'3',值为112的属性,遍历obj属性名并输出

const obj = {
    'name': 'Bruse', // 普通属性
    'age': 16, // 普通属性
    2: 'obj2', // 排序属性
    '3': 112 // 转换,也是排序属性
}
obj[1] = 'obj1' // 排序属性
obj[99] = 'obj99' // 排序属性
obj[10] = 'obj10' // 排序属性
for (const key in obj) {
    console.log(key)
}

输出

1
2
3
10
99
name
age

可以看到即便在定义属性键时,是字符串类型的数字3,也会做一下转换,将其转换为排序属性

排序属性 VS 普通属性

存储方式

首先排序属性普通属性在对象中的存储方式不一样

可以简单地理解为obj对象中,分别有着elementsproperties两个内置的属性,elements可以理解为是一个数组,而数字键则是属性在该数组中的下标,也就是内存偏移量。通过下标访问数组中的某个元素是很快的。当我们要访问obj.1时,会先从obj对象中内置的elements属性中通过1这个下标进行查询,最终找到obj[1]的值为obj1

properties可以理解为是一个Map,而字符串键则是该Map中的keyvalue则是对应的属性值。当访问obj.name时,会先从elements中进行查找,结果是找不到,然后再从properties中进行查找,但从properties中查找则没有elements中查找快,因为elements可以通过计算偏移量来进行访问,但是properties要hash计算访问。

对象内属性

其实上图还并不是全貌,因为即便有着elementsproperties分别存放排序属性和普通属性,但是无论访问排序属性和普通属性[排序属性访问速度优于普通属性],都是要先访问obj这个对象的elementsproperties内置属性,然后再通过属性键访问到具体的属性值,其实就相当于obj.name = obj.properties.name,还存在优化空间,这个时候对象内属性就出场了。

对象内属性其实就是被保存到对象自身的常规属性,也就是真正意义上的让obj.name不再等于obj.properties.name,而是真正的所见即所得obj.name

代码测试

为了方便理解,把之前的代码稍作修改

class People {
   constructor() {
       this.name = 'Bruse'
       this.age = 16
       this[2] = 'obj2'
   }
}
const obj = new People()
obj[1] = 'obj1' // 排序属性
obj[99] = 'obj99' // 排序属性
obj[10] = 'obj10' // 排序属性
debugger  // 避免执行过快,导致还来不及生成内存快照,obj对象就已被回收
for (const key in obj) {
   console.log(key)
}

F12打开浏览器开发者工具,当debugger阻塞住代码往下执行时,点击Memory,生成内存快照

可以看到obj这个对象中存在内置属性elements和一些内属性name[10][99][1][2],暂时并没有内置属性properties

首先因为obj属性并不多,此时对象内属性的数量还比较少,所以此时并不需要内置属性properties来存放普通属性,而是将name等普通属性当做是对象的内置属性存放即可,访问速度比访问elementsproperties更快。

Tips:不要看到elements中10、99排在1、2前面,就以为elements不是按照数字升序排列的,因为输出之后你会发现其实还是1、2、10、99这样的顺序,至于为什么在调试工具里看起来顺序有点不太一致,我也不知道...

添加更多的排序属性

接下来给obj塞入更多的排序属性

class People {
    constructor() {
        this.name = 'Bruse'
        this.age = 16
    }
}
const obj = new People()
for (let i = 0; i < 20; i++) {
    obj[i] = `obj${i}`
}
debugger
for (const key in obj) {
    console.log(key)
}

再看看看obj的变化,首先elements属性中的元素变多了

同时也并没有出现内置属性properties

因为只是增加了更多的排序属性,并没有突破内置属性(内置常规属性)的数量限制

添加更多的普通属性

接下来再塞入更多的普通属性

class People {
    constructor() {
        this.name = 'Bruse'
        this.age = 16
        for (let i = 0; i < 20; i++) {
            this[`obj${i}`] = i
        }
    }
}
const obj = new People()
debugger
for (const key in obj) {
    console.log(key)
}

可以看到在这个时候obj的内置属性properties终于出现了

只不过貌似并不像elements那样方便预览其中的属性...后来经过一番查找和尝试,终于是找到了解决办法... 也很简单,就是生成内存快照时多做一步操作,勾上“在快照中添加数字值”

再次内存分析,好吧...因为生成20个常规变量的缘故,貌似也还没达到内置属性的限制...

将数量调整到50个,可以看到elementsproperties都有了相应存放的变量

for (let i = 0; i < 50; i++) {
    this[i] = `obj${i}`
    this[`obj${i}`] = i
}

困惑

单纯在Chrome上进行内存分析的话,貌似即便超出了内属性数量限制,那多出来的一部分普通属性,也非常直白地展示在properties之外,这个暂时不太清楚...

总结

把上述排序属性``普通属性``内属性结合到一块来看,那么JavaScript中的对象属性存放结构应该如下图所示

首先是为了提高访问速度,对象obj本身就会有一定空间存放内属性,在访问内属性时,可以直接跳过访问elementsproperties这一步,直接访问到该属性的值。

但是内属性是有一定数量限制的,所以当超出了限制后,剩下的普通属性会被存放到properties中,而properties有点像Map,在进行属性访问的时候,需要计算出键的hash值,然后才能访问到具体的属性值。

elements则是存放排序属性用的,有点像Array,数字键即数组中的下标,所有元素按数字升序进行排序,访问属性值时则是通过下标进行访问,访问速度会比properties要快一些。

propertieselements两种存储方式之间的VS,本质上就像是数据结构中的MapArray之间的VS。

以上就是JavaScript中普通属性和排序属性详解的详细内容,更多关于JavaScript属性的资料请关注脚本之家其它相关文章!

相关文章

  • javascript中闭包概念与用法深入理解

    javascript中闭包概念与用法深入理解

    这篇文章主要介绍了javascript中闭包概念与用法,结合实例形式深入分析了javascript中闭包的概念、使用方法与相关注意事项,需要的朋友可以参考下
    2016-12-12
  • 微信小程序:报错(in promise) MiniProgramError

    微信小程序:报错(in promise) MiniProgramError

    这篇文章主要介绍了微信小程序:报错(in promise) MiniProgramError,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-10-10
  • 微信小程序实现手风琴折叠面板

    微信小程序实现手风琴折叠面板

    这篇文章主要为大家详细介绍了微信小程序实现手风琴折叠面板,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-05-05
  • JS中for,for...in,for...of和forEach的区别和用法实例

    JS中for,for...in,for...of和forEach的区别和用法实例

    JS遍历数组(循环数组)的方式有多种,可以使用传统的for循环,也可以使用升级版的for in循环,还可以使用Array类型的forEach() 方法,这篇文章主要给大家介绍了关于JS中for、for...in、for...of和forEach的区别和用法的相关资料,需要的朋友可以参考下
    2021-11-11
  • 使用JavaScript 将数据网格绑定到 GraphQL 服务的操作方法

    使用JavaScript 将数据网格绑定到 GraphQL 服务的操作方法

    GraphQL是管理JavaScript应用程序中数据的优秀工具,本教程展示了GraphQL和SpreadJS如何简单地构建应用程序, GraphQL 和 SpreadJS都有更多功能可供探索,因此您可以做的事情远远超出了这个示例,感兴趣的朋友一起看看吧
    2023-11-11
  • 在webstorm中配置less的方法详解

    在webstorm中配置less的方法详解

    这篇文章主要介绍了在webstorm中配置less的方法,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2020-09-09
  • Summernote实现图片上传功能的简单方法

    Summernote实现图片上传功能的简单方法

    下面小编就为大家带来一篇Summernote实现图片上传功能的简单方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-07-07
  • js获取浏览器高度 窗口高度 元素尺寸 偏移属性的方法

    js获取浏览器高度 窗口高度 元素尺寸 偏移属性的方法

    下面小编就为大家带来一篇js获取浏览器高度 窗口高度 元素尺寸 偏移属性的方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-11-11
  • 再论Javascript下字符串连接的性能

    再论Javascript下字符串连接的性能

    这是个老话题了,之所以再拿出来说,是因为浏览器一直在进步,以前最好的方法现在不一定是最好的。
    2011-03-03
  • js保留两位小数方法总结

    js保留两位小数方法总结

    本篇文章给大家总结了js保留两位小数的各种方法以及每种方法的实例代码分析,如果大家对此有需要,一起来学习下js保留两位小数的方法吧。
    2018-01-01

最新评论