在之前的文章中,我们提到过三个遍历对象属性的原生 API ——Object.keys(),Object.values(),Object.entries().
这三个 API 很常用,但是它们有自己的局限,在上文中提到过,首先它们三者都对 "不可遍历" 的属性 "无可奈何",其次对于 "Symbol" 属性,它们同样也无法遍历.所以在这里我们要 系统 的上述一下对象属性遍历的内容.
刚刚我们提到过,对象的属性有时是不可枚举的,那么什么时候属性不可枚举,为什么要存在这种不可枚举的属性呢?这些问题都牵扯到早起 Java Script 的历史.
以 JavaScript 为首的 ECMAScript 语言不通与 Java 等面向对象的语言.在 Java 语言的设计里,"对象" 这一概念是作为 "类" 的衍生品存在的,在 Java 中 "对象" 是 "类" 的实例化,就像是鸡和蛋的关系一样,没有鸡就没有蛋.但是 JavaScript 不同,在设计之初,JavaScript 是没有类这一概念的.
在早起的 web 时代,"对象" 是 "程序员手写的大括号",小工程一般也就是几十个对象之间的互动罢了,如果工程较大的话,那就写一个函数,在函数(构造函数)中 "return 对象",在对象之间设定原型关系,组成原型链来继承属性和方法(提高复用性).
之后过去了几年(ES3 的时代),web 工程的需求和工作量激增,之前的三板斧不好用了,在这个时间段里,如果工程里的对象都是手写的话,程序员早就累死了,没办法,一些高端的 web 程序员,巧妙地用 "构造函数" 和 "this 指针" 实现了 "类",用 "apply 和 call" 方法实现了 "类的继承",进而实现 "接口","抽象类" 等基础概念和 "众多设计模式".此时前端程序员开始用 "类似传统面向对象" 的工作方式来开发 web 程序.
但巧妇难为无米之炊,在应用的层面上实现语言层该有的东西,总是难以尽善尽美,例如,虽然技术高超的 web 程序员能够实现 "类" 这一机制,但是无法实现类中 "私有成员" 这一机制(事实上直到今天 ES6 里也没有实现这一功能).程序员们此时大多彼此约定,变量名中以下划线开头的变量,是我代码里的私有便令,你写程序的时候就是看见了它也别用它.
再后来 ES5.1 的时代到来了.此时正值 ES4 刚刚宣布开发失败不旧,没有什么强有力的语法更新,class 等机制也没有登台,ECMAScript 组织就在 Object 上面增加了一些补丁,其中很重要的一点就是,对象的属性开始有了 "特性" 这一概念.
所谓 "属性的特性" 指的是属性的一些性质,例如 "可读性","可写性","可枚举性" 和 "可继承性" 等.此时 "class" 仍然只是一种开发技巧,在语言的层面上,ECMA 仍然认为,ECMAScript 是一种基于对象开发的语言,所以,ECMA 就说,如果对于一个对象来说,我有属性 A,但是你不可以枚举它,不可以 write 它,甚至你不能 read 它,那它和 "私有" 的又有什么区别呢?
故此,在 ECMAScript5.1 版本中,增加了一些设置属性特性的 API,对象的属性有些变得不可枚举.进而造成了之前我们说的遍历对象属性时的那些问题.
那么总结一下,属性遍历的方法有几种呢?——五种:
利用 for...in 循环遍历属性;——循环对象自身的属性,对象继承来的属性,不会遍历不可枚举的属性,不会遍历 Symbol 属性.利用 Object.keys(obj),Object.values(Obj),Object.entries(Obj) 遍历属性;——详见上一篇文章利用 Object.getOwnPropertyName(Obj) 遍历属性;——返回一个数组,返回对象自身所有的除了 Symbol 属性之外的所有属性(包括不可枚举的属性)的键名.利用 Object.getOwnPropertySymbols(Obj) 遍历属性;——返回一个数组,包含对象自身所有 Symbol 属性键名.利用 Relect.ownKeys(obj) 遍历属性;返回一个数组,包含对象自身所有属性,无论键名是否是 Symbol,也不管属性是否可枚举. 以上的 5 中方法,在遍历对象键名的时候,都遵循同样的遍历次序,规则如下: 首先遍历所有数值键,按照从小到大的顺序排列 其次遍历所以字符串键,按照加入时间顺序,升序排列. 最后遍历所有 Symbol 键,按照时间顺序,升序排列.
来源: https://www.2cto.com/kf/201801/711061.html