ES 提案:Object.fromEntries()

提案 “Object.fromEntries” (由 Darien Maillet Valentine, Jordan Harband 和 Kevin Gibbons 提出)处在 stage 4 阶段,因此是 ES2019 的一部分。这篇博客解释它是如何工作的。

#Object.fromEntries() 与 Object.entries()

通过可迭代的 [key, value] 部分,Object.fromEntries() 创建一个对象:

Object.fromEntries([['foo', 1], ['bar', 2]]);
// {foo: 1, bar: 2}

它是 Object.entries() 的反面:

const obj = {
  foo: 1,
  bar: 2,
};

Object.entries(obj);
// [['foo',1], ['bar',2]]

结合 Object.entries()Object.fromEntries() 帮助实施对象相关的各种操作,继续阅读示例。

#例子

#_.pick(object, ...keys)

pick() 删除 object 中不包含 key 的所有属性。删除是非破坏性的:pick() 返回新的副本而没有破坏原对象。举例来说:

const address = {
  street: 'Evergreen Terrace',
  number: '742',
  city: 'Springfield',
  state: 'NT',
  zip: '49007',
};

pick(address, 'street', 'number');
// {
//   street: 'Evergreen Terrace',
//   number: '742',
// }

我们可以按照以下实现 pick()

function pick(object, ...keys) {
  const filteredEntries = Object.entries(object).filter(([key, _value]) =>
    keys.includes(key),
  );
  return Object.fromEntries(filteredEntries);
}

#_.invert(object)

invert() 非破坏性的交换 object 的键和值:

invert({ a: 1, b: 2, c: 3 });
// {1: 'a', 2: 'b', 3: 'c'}

我们可以像这样实现它:

function invert(object) {
  const mappedEntries = Object.entries(object).map(([key, value]) => [
    value,
    key,
  ]);
  return Object.fromEntries(mappedEntries);
}

#_.mapObject(object, iteratee, context?)

mapObject() 就像 Array 的方法 .map(),但是作用于对象:

mapObject({ x: 7, y: 4 }, (value) => value * 2);
// {x: 14, y: 8}

这是一种实现:

function mapObject(object, callback, thisValue) {
  const mappedEntries = Object.entries(object).map(([key, value]) => {
    const mappedValue = callback.call(thisValue, value, key, object);
    return [key, mappedValue];
  });
  return Object.fromEntries(mappedEntries);
}

#_.findKey(object, predicate, context?)

findKey() 返回其第一个属性的键 predicate 返回 true

const address = {
  street: 'Evergreen Terrace',
  number: '742',
  city: 'Springfield',
  state: 'NT',
  zip: '49007',
};

findKey(address, (value, _key) => value === 'NT');
// 'state'

我们可以按照以下实现它:

function findKey(object, callback, thisValue) {
  for (const [key, value] of Object.entries(object)) {
    if (callback.call(thisValue, value, key, object)) {
      return key;
    }
  }
  return undefined;
}

#实现

Object.fromEntries() 可以按照下面代码实现(忽略了一些检查):

function fromEntries(iterable) {
  const result = {};
  for (const [key, value] of iterable) {
    let coercedKey;
    if (typeof key === 'string' || typeof key === 'symbol') {
      coercedKey = key;
    } else {
      coercedKey = String(key);
    }
    Object.defineProperty(result, coercedKey, {
      value,
      writable: true,
      enumerable: true,
      configurable: true,
    });
  }
  return result;
}

通过 npm 包 Object.fromEntries 可获得一个官方的填充。

#更多关于 Object.fromEntries 的详情

  • 重复键:如果你提供相同的键多次,最后面的键将覆盖前面:
    Object.fromEntries([['a', 1], ['a', 2]]);
    // { a: 2 }
    
  • Symbol 键:即使 Object.entries() 忽略 Symbol 最为键的属性,Object.fromEntries() 依然接受 Symbol 作为键。
  • 键的强制转换:[key, value] 部分的键被强制转化为属性键:字符串和 symbol 类型以外的值被强制转化为字符串。
  • Iterables 与 Arrays:
    • Object.entries() 返回一个数组(与 Object.keys() 一致),它是以 [key,value] 组成的二维数组。
    • Object.fromEntries() 是灵活的:它接受 iterables(包括 Arrays 并且与之一致 new Map())。它的 [key,value] 对只需要是具有键 '0' 和 '1'(包括二维数组)属性的对象。
  • 仅支持可枚举的数据属性:如果要创建不可枚举的属性和/或非数据属性,则需要使用 Object.defineProperty() 或 Object.defineProperties()
译者注
关于迭代器 Iterables,详见 MDN 的解释。