current position:Home>The "weird" feature of Object.keys is worth collecting!

The "weird" feature of Object.keys is worth collecting!

2022-09-23 10:05:55The front nine south

先从‘诡异’start with the problem

  • 例1: 纯Number类型的属性
const obj = {
  1: 1,
  6: 6,
  3: 3,
  2: 2
}
console.log('keys', Object.keys(obj)) 
// ['1', '2', '3', '6']

返回的keyWhy is it automatically sorted in ascending order??

  • 例2: 纯String类型的属性
const obj2 = {
  a: 'a',
  c: 'c',
  f: 'f',
  b: 'b',
}
console.log(Object.keys(obj2))
// ['a', 'c', 'f', 'b']

Why is not automatic sorting again here?

Do you feel confused when you see this??话不多说,Let's check the documentation first,看看mdn上对Object.keys的描述:

Object.keys() 方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致 .

emm,However it doesn't say exactly in which order it is returned.

探索

Since I can't find it in the documentation,Then we will study step by step

Object.keys的polyfill的实现

if (!Object.keys) {
  Object.keys = (function () {
    var hasOwnProperty = Object.prototype.hasOwnProperty,
        hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'),
        dontEnums = [
          'toString',
          'toLocaleString',
          'valueOf',
          'hasOwnProperty',
          'isPrototypeOf',
          'propertyIsEnumerable',
          'constructor'
        ],
        dontEnumsLength = dontEnums.length;

    return function (obj) {
      if (typeof obj !== 'object' && typeof obj !== 'function' || obj === null) throw new TypeError('Object.keys called on non-object');

      var result = [];

      for (var prop in obj) {
        if (hasOwnProperty.call(obj, prop)) result.push(prop);
      }

      if (hasDontEnumBug) {
        for (var i=0; i < dontEnumsLength; i++) {
          if (hasOwnProperty.call(obj, dontEnums[i])) result.push(dontEnums[i]);
        }
      }
      return result;
    }
  })()
};

Object.keys的polyfill的实现,We can find that it actually usesfor...in来实现的.那我们就可以去查找for...inTraversed the order of the rules.However, it does not describe the order of traversal.,Then we can only findECMAScript的规范了.

Object.keys的规范定义

  1. 调用ToObject(O)将结果赋值给变量obj
  2. 调用EnumerableOwnPropertyNames(obj, "key")将结果赋值给变量nameList
  3. 调用CreateArrayFromList(nameList)得到最终的结果

第一步:将参数转换成Object(ToObject(O)

因为Object.keys内部会调用ToObject(O)方法,So it doesn't just accept object parameters,Can also accept other types of parameters,The table below isToObjectAccording to different types of values ​​intoObject的映射:

参数类型结果
Undefined抛出TypeError
Null抛出TypeError
Number返回一个新的 Number 对象
String返回一个新的 String 对象
Boolean返回一个新的 Boolean 对象
Symbol返回一个新的 Symbol 对象
Object直接将Object返回

我们通常给Object.keysThe passed parameter will be an object,But we can also take a look at what the return value of other types of values ​​will look like?

  • Number
console.log(Object.keys(123)) // []

返回的是空数组,这是因为new Number(123)no extractable properties

num1.png

  • String
console.log(Object.keys('123')) // [ '0', '1', '2' ]

The reason why the string is not returning an empty array,是因为new String('123')There are properties that can be extracted

str1.png

第二步:Get a list of properties from the converted objectproperties.(The order depends on here)

对象属性列表是通过 EnumerableOwnPropertyNames 获取的,One of the more important is to call the internal method of the objectOwnPropertyKeys获得对象的ownKeys(这些内容可以在ECMAScriptfound in the specification,就不展开介绍了,We focus on sorting)

The [[OwnPropertyKeys]] internal method of an ordinary object O takes no arguments. It performs the following steps when called:

  1. Return ! OrdinaryOwnPropertyKeys(O).

通过上面的介绍,我们可以发现keysThe ordering depends on OrdinaryOwnPropertyKeys(O)

1.png

翻译过来就是:

  1. 创建一个空的列表用于存放 keys
  2. 将所有合法的数组索引按升序的顺序存入
  3. 将所有字符串类型索引按属性创建时间以升序的顺序存入
  4. 将所有 Symbol 类型索引按属性创建时间以升序的顺序存入
  5. 返回 keys

注意:属性列表properties为List类型(List类型ECMAScript规范类型

第三步:将List类型的属性列表properties转换为Array得到最终的结果.

将ListThe type's property list is converted toArray类型非常简单:

  1. 先声明一个变量array,值是一个空数组
  2. Cyclic property list,将每个元素添加到array
  3. array返回

总结

Object.keysReturned object property order

  • 将所有合法的数组索引按升序排序

  • 将所有字符串类型索引Sort by property creation time in ascending order

  • 将所有 Symbol 类型索引Sort by property creation time in ascending order

Legal array indices refer to positive integers,Negative numbers or floating-point numbers are always treated as strings.严格来说对象属性没有数字类型的,无论是数字还是字符串,都会被当做字符串来处理.

看题

const obj = {}
obj[-1] = -1
obj[1] = 1
obj[1.1] = 1.1
obj['2'] = '2'
obj['c'] = 'c'
obj['b'] = 'b'
obj['a'] = 'a'
obj[2] = 2
obj[Symbol(1)] = Symbol(1)
obj[Symbol('a')] = Symbol('a')
obj[Symbol('b')] = Symbol('b')
obj['d'] = 'd'
console.log(Object.keys(obj))

经过上面对Object.key特性的介绍,I'm sure everyone won't make a mistake againObject.keysthe output order.

答案:

[ '1', '2', '-1', '1.1', 'c', 'b', 'a', 'd' ]

Seeing the answer, do many students have many questions??

How to understand if an object property is a positive integer or a string?

First we said aboveLegal array indices refer to positive integers,Negative numbers or floating-point numbers are always treated as strings.严格来说对象属性没有数字类型的,无论是数字还是字符串,都会被当做字符串来处理.

So the above is only1,'2',2is a valid array index,But we know that in fact they will all be converted to strings,所以后面的2会将前面的'2'覆盖,Then they are sorted in ascending order.Then negative numbers and floating-point numbers are treated as strings, sorted by attribute creation time in ascending order.This will give you the answer above.

为什么没有Symbol类型?

因为在 EnumerableOwnPropertyNames 的规范中规定了返回值只应包含字符串属性(上面说了数字其实也是字符串).

我们也可以在MDNView about Object.getOwnPropertyNames() 的描述.

Object.getOwnPropertyNames() 方法返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括 Symbol 值作为名称的属性)组成的数组.

所以 Symbol 属性是不会被返回的,如果要返回 Symbol 属性可以用 Object.getOwnPropertySymbols().

原文首发地址点这里,欢迎大家关注公众号 「前端南玖」,如果你想进前端交流群一起学习,请点这里

我是南玖,我们下期见!!!

copyright notice
author[The front nine south],Please bring the original link to reprint, thank you.
https://en.chowdera.com/2022/266/202209230958319876.html

Random recommended