【问题标题】:ES6 safe to use static class variable as key for Map?ES6 可以安全地使用静态类变量作为 Map 的键吗?
【发布时间】:2017-12-26 23:08:12
【问题描述】:

babel-preset-stage-0中,我们可以声明静态类变量如下:

class Constants {
  static COUNTRY = Object.freeze({
      NAME: 'Germany',
      DESCRIPTION: 'placeholder',
  })
}

使用 Constants.COUNTRY 作为 ES6 MapSet 的密钥是否安全?

例如。

const map = new Map();
map.add(Constants.COUNTRY, something);

是否保证map.get(Constants.COUNTRY) 将始终返回something

性能是否与使用字符串作为键一样好?将Constants.COUNTRY 用作eventKey(引导组件属性)用于NavItem 是否安全?

将其声明为变量而不是类是否也更合适? 即

const Constants = Object.freeze({
  COUNTRY: Object.freeze({
      NAME: 'Germany',
      DESCRIPTION: 'placeholder',
  })
}) 

【问题讨论】:

  • 只有当Constants.COUNTRY 的值永远不会改变并且没有人从地图中删除该键时,才能保证始终返回您的数据。静态类只是一个变量,与您的 Constants 变量替代品没有什么不同。
  • Object.freeze 防止Constants.COUNTRY 以任何方式被修改,但可以为静态类变量重新分配它......所以我想后一个版本将它声明为常量 Object 是比将它声明为一个类更好。
  • Object.freeze() 防止更改对象(这对地图无关紧要,因为它仍然是同一个对象)。 Object.freeze() 不会阻止 Constants.COUNTRY = "hello",这在访问 map.get(Constants.COUNTRY) 时很重要。
  • 是的,刚刚注意到;鉴于此,将其声明为 const 对象比作为类更安全...
  • 是的,您的第二个表单(使用const)将防止Constants.Country 被更改。我想知道您是否可以在第一个示例中使用Object.freeze(Constants)

标签: javascript ecmascript-6 babeljs ecmascript-next


【解决方案1】:

是否保证 map.get(Constants.COUNTRY) 总是会返回一些东西?

要使 map.get(Constants.COUNTRY) 始终返回您的原始值,必须满足以下条件。

  1. 您必须确保永远不能为 Constants.COUNTRY 分配不同的值,因为 .COUNTRY 属性被重新分配,或者因为 Constants 对象被替换为具有不同 .COUNTRY 的其他对象属性值。

  2. 您必须确保没有人可以从 map 对象中删除该密钥。

如果您能保证这两件事,那么是的map.get(Constants.COUNTRY) 将始终返回您想要的值。但是,如果其中任何一个不一定正确,那么您就无法保证始终从地图中获得价值。

您可以通过冻结Constants 对象或将该属性设置为可配置来确保不能删除或写入Constants.COUNTRY。为确保Constants 对象无法被替换,最好将其设为const,就像在您的第二个代码块中一样。

我不知道有什么方法可以确保没有人可以调用 map.delete(Constants.COUNTRY),除非将 map 对象保持为私有,这样外来代码就无法访问它。

如果您有任何理由想要阻止枚举映射中的键(以使某人更难发现键),那么您可以使用WeakMap 而不是Map

性能是否和使用字符串作为键一样好?

您必须测试特定的 Javascript 实现才能确定性能。没有必要的实现原因表明其中一个应该更快 - 它仅取决于实现的内部结构。

我创建了一个jsPerf test case 来比较字符串查找和对象查找。欢迎提供有关改进测试/测量方式的反馈,但使用当前方案,我在地图中创建 10,000 个字符串键和 10,000 个对象键,然后比较访问其中的 1000 个,我发现了不同的结果。

Chrome is ~20% slower to access the object keys.
Firefox is ~20% slower to access the string keys.
Edge is ~27% slower to access the string keys.

将其声明为变量而不是类是否也更合适?

如前所述,您的第二个const 表单的优点是不能重新分配Constants

【讨论】:

  • 添加了字符串键与对象键的性能测量数据。
  • 刚刚发现将其声明为 const 的缺点(如果我没记错的话,与提升有关):我无法在 Constants 中添加新的变量/函数Constants.COUNTRY,所以我必须在Constants 之前声明COUNTRY,然后将其添加到Constants,这不是DRY。我猜类会更好......任何决定导入一个类并重新分配它的人都不应该编写代码。
  • @Avery235 - const Constants 仅表示无法重新分配 Constants。它与将属性分配给Constants 对象无关。由于您的Object.freeze(),您被阻止了。您可以在完成构建后冻结对象。
  • class Constant { static a = 1; static b = Constant.a } 有效,但 const Constant = { a: 1, b: Constant.a } 无效。我需要做const a = 1; const Constant = { a: a, b: a }
  • 在声明const Constant = {...} 时,不能在静态声明中引用Constant(因为它在声明中尚不存在)。但是您可以声明const Constant = {...},然后在该声明之后的以下几行中添加更多属性,例如Constant.a = Constant.foo。然后,在添加所有你想要的属性之后,你可以Object.freeze(Constant)
【解决方案2】:

您可以使用WeakMapCOUNTRY 作为键,使用something 作为值。不能删除用const 声明的变量。随着您使用Object.freeze()wm.get(COUNTRY) 应始终返回something

  const wm = new WeakMap;

  const COUNTRY = Object.freeze({
    NAME: 'Germany',
    DESCRIPTION: 'placeholder',
  });

  wm.set(COUNTRY, "something");

  // error when "use strict"
  delete wm;
  delete COUNTRY;

  COUNTRY.NAME = 123;

  console.log(
    wm.get(COUNTRY)
  );
  
  console.log(
    COUNTRY
  );
  
  console.log(
    wm
  );

如果需要一个无法删除或更改的变量,您可以使用const 请参阅Is it possible to delete a variable declared using const?JSON

"use strict";

// `Constants` cannot be changed or deleted
const Constants = `{
  "NAME": "Germany",
  "DESCRIPTION": "placeholder"
}`;

console.log(
  JSON.parse(Constants)
);

// delete Constants;
/*
Error: {
  "message": "Uncaught SyntaxError: Delete of an unqualified identifier in strict mode."
}
*/

【讨论】:

  • 在这种情况下,weakMap 与常规 Map 相比有何优势?
  • @jfriend00 无法枚举密钥,如果这可以说是一种优势;取决于 OP 在第一个实例中最终试图通过该模式实现的目标。 OP 也可以使用const 而不是class,尽管Answer 的那部分不应影响WeakMapMap 的使用。 OP提到了“性能”,垃圾收集。
  • 它在哪里作为要求或什至希望无法从map 枚举密钥?
  • @jfriend00 Answer 的文本中有“优势”吗?
  • 您提供的答案使用WeakMap。我不明白为什么它是一个有趣的答案,或者为什么 WeakMap 比 OP 已经在使用的 Map 更有助于回答这个问题。我只是觉得这是一个无法解释的令人困惑的答案。并且wm.delete(COUNTRY) 将使wm.get(COUNTRY) 不再返回任何有用的信息,因此您的最后一句话仅适用于该限定词(在我的 cmets 中已经提到,但在您的答案中没有提到)。
猜你喜欢
  • 2018-05-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-09
  • 2021-05-02
  • 2012-05-14
  • 1970-01-01
相关资源
最近更新 更多