【问题标题】:Declaring static constants in ES6 classes?在 ES6 类中声明静态常量?
【发布时间】:2015-12-15 07:40:04
【问题描述】:

我想在class 中实现常量,因为这是在代码中定位它们的意义所在。

到目前为止,我一直在使用静态方法实现以下解决方法:

class MyClass {
    static constant1() { return 33; }
    static constant2() { return 2; }
    // ...
}

我知道有可能摆弄原型,但许多人建议不要这样做。

有没有更好的方法在 ES6 类中实现常量?

【问题讨论】:

  • 就我个人而言,我只是使用大写的 VARNAMES,并告诉自己不要碰它们 ;)
  • @twicejr 我认为这不一样,因为静态变量可以在不先实例化该类的对象的情况下访问?

标签: javascript class constants ecmascript-6


【解决方案1】:

你可以做以下几件事:

模块中导出const。根据您的用例,您可以:

export const constant1 = 33;

并在必要时从模块中导入。或者,基于您的静态方法理念,您可以声明一个 static get accessor

const constant1 = 33,
      constant2 = 2;
class Example {

  static get constant1() {
    return constant1;
  }

  static get constant2() {
    return constant2;
  }
}

这样,你就不需要括号了:

const one = Example.constant1;

Babel REPL Example

然后,正如您所说,由于 class 只是函数的语法糖,您可以像这样添加不可写属性:

class Example {
}
Object.defineProperty(Example, 'constant1', {
    value: 33,
    writable : false,
    enumerable : true,
    configurable : false
});
Example.constant1; // 33
Example.constant1 = 15; // TypeError

如果我们能做这样的事情可能会很好:

class Example {
    static const constant1 = 33;
}

但不幸的是,这个class property syntax 仅在 ES7 提案中,即使那样它也不允许将 const 添加到属性中。

【讨论】:

  • 是否有任何确认静态属性被计算一次这样的事情,或者使用 IIFE 并在 IIFE 中手动添加属性以避免重复构造返回值是否更安全。我担心如果 getter 的结果真的很重,比如 100000 个条目的 JSObject,那么可怜的 getter 将不得不在每次调用 getter 时构造它。它很容易通过 performance.now/date diff 进行测试,但它可能以不同的方式实现,它肯定更容易将 getter 实现为文字评估而不是高级决策,无论它是否恒定。
  • 虽然上面巧妙地为类添加了一个常量属性,但该常量的实际值是在类定义“{}”“之外”,这确实违反了封装的定义之一。我想只在类“内部”定义一个常量属性就足够了,在这种情况下不需要 get。
  • @NoChance 好点。那只是说明性的。如果需要,getter 方法没有理由不能完全封装值。
  • 期待使用 ES7 提案,因为在我看来它更自然,并且等同于大多数 OO 语言。
  • super 关键字不仅仅是语法糖
【解决方案2】:
class Whatever {
    static get MyConst() { return 10; }
}

let a = Whatever.MyConst;

似乎对我有用。

【讨论】:

  • 这可以在类中以普通方法访问吗?
  • @PirateApp 您可以作为静态方法在任何地方访问它,甚至可以从类的实例内部访问它。但是,由于它是静态的,因此您不能在 Whatever 实例中使用 this.MyConst,因此您始终必须这样编写:Whatever.MyConst
  • 或 this.constructor.MyConst
  • 静态吸气剂可能是目前最干净的解决方案。
  • 我也这样做了,但它从根本上与“静态”的定义相冲突,因为返回的值不在实例之间共享。这很方便,但最终是一个糟糕的选择
【解决方案3】:

我正在使用babel,以下语法对我有用:

class MyClass {
    static constant1 = 33;
    static constant2 = {
       case1: 1,
       case2: 2,
    };
    // ...
}

MyClass.constant1 === 33
MyClass.constant2.case1 === 1

请考虑您需要预设"stage-0"
要安装它:

npm install --save-dev babel-preset-stage-0

// in .babelrc
{
    "presets": ["stage-0"]
}

更新:

目前使用stage-3

【讨论】:

  • 问题是常量是可重新分配的。欧普不想这样
  • 仅供参考,现在在 babel stage-2
  • 那些不是常量
  • @CodingIntrigue 在课堂上调用Object.freeze() 会解决这个问题吗?
  • @Antimony 我还没有测试过,但我会这么认为。问题是它将适用于类的所有属性。也是非静态的。
【解决方案4】:

this document 中声明:

没有(故意)直接的声明方式来定义原型数据属性(方法除外)类属性或实例属性

这意味着它是故意这样的。

也许你可以在构造函数中定义一个变量?

constructor(){
    this.key = value
}

【讨论】:

  • 是的,这可以工作。另外,我想提一下,该构造函数在创建实例时调用,并且对于每个实例,this.key 都不相同。静态方法和属性允许我们直接从类中使用它们,而无需创建实例。静态方法/属性有优缺点。
  • 常量应该是不可变的。在构造期间分配给对象的属性将产生可以修改的属性。
【解决方案5】:

也可以在您的类(es6)/构造函数(es5)对象上使用Object.freeze 使其不可变:

class MyConstants {}
MyConstants.staticValue = 3;
MyConstants.staticMethod = function() {
  return 4;
}
Object.freeze(MyConstants);
// after the freeze, any attempts of altering the MyConstants class will have no result
// (either trying to alter, add or delete a property)
MyConstants.staticValue === 3; // true
MyConstants.staticValue = 55; // will have no effect
MyConstants.staticValue === 3; // true

MyConstants.otherStaticValue = "other" // will have no effect
MyConstants.otherStaticValue === undefined // true

delete MyConstants.staticMethod // false
typeof(MyConstants.staticMethod) === "function" // true

试图改变类会给你一个软失败(不会抛出任何错误,它根本没有效果)。

【讨论】:

  • 对于我们这些来自其他语言的人来说,软失败是相当可怕的——只是适应工具对我们查找错误没有多大帮助的想法,现在即使运行时也无济于事. (否则我喜欢你的解决方案。)
  • 我喜欢 Object.freeze() 强制执行不变性,并且最近一直在使用它。只是不要忘记递归应用它!
【解决方案6】:

也许只是把你所有的常量放在一个冻结的对象中?

class MyClass {

    constructor() {
        this.constants = Object.freeze({
            constant1: 33,
            constant2: 2,
        });
    }

    static get constant1() {
        return this.constants.constant1;
    }

    doThisAndThat() {
        //...
        let value = this.constants.constant2;
        //...
    }
}

【讨论】:

  • 静态函数不能使用变量'this'。
【解决方案7】:

您可以使用 ES6 类的奇怪特性创建一种在类上定义静态常量的方法。由于静态是由其子类继承的,因此您可以执行以下操作:

const withConsts = (map, BaseClass = Object) => {
  class ConstClass extends BaseClass { }
  Object.keys(map).forEach(key => {
    Object.defineProperty(ConstClass, key, {
      value: map[key],
      writable : false,
      enumerable : true,
      configurable : false
    });
  });
  return ConstClass;
};

class MyClass extends withConsts({ MY_CONST: 'this is defined' }) {
  foo() {
    console.log(MyClass.MY_CONST);
  }
}

【讨论】:

  • 这正是 OP 所要求的,据我所知,这是整个答案列表中唯一正确且完整的答案。干得好。
【解决方案8】:

就像https://stackoverflow.com/users/2784136/rodrigo-botti 所说,我想你正在寻找Object.freeze()。下面是一个具有不可变静态的类的示例:

class User {
  constructor(username, age) {
    if (age < User.minimumAge) {
      throw new Error('You are too young to be here!');
    }
    this.username = username;
    this.age = age;
    this.state = 'active';
  }
}

User.minimumAge = 16;
User.validStates = ['active', 'inactive', 'archived'];

deepFreeze(User);

function deepFreeze(value) {
  if (typeof value === 'object' && value !== null) {
    Object.freeze(value);
    Object.getOwnPropertyNames(value).forEach(property => {
      deepFreeze(value[property]);
    });
  }
  return value;
}

【讨论】:

    【解决方案9】:

    我做到了。

    class Circle
    {
        constuctor(radius)
        {
            this.radius = radius;
        }
        static get PI()
        {
            return 3.14159;
        }
    }
    

    PI 的值不会被更改,因为它是从函数返回的值。您可以通过 Circle.PI 访问它。任何分配给它的尝试都会以类似于尝试通过 [] 分配给字符串字符的方式简单地丢弃在地板上。

    【讨论】:

      【解决方案10】:

      您可以通过冻结类将“常量”设为只读(不可变)。例如

      class Foo {
          static BAR = "bat"; //public static read-only
      }
      
      Object.freeze(Foo); 
      
      /*
      Uncaught TypeError: Cannot assign to read only property 'BAR' of function 'class Foo {
          static BAR = "bat"; //public static read-only
      }'
      */
      Foo.BAR = "wut";
      

      【讨论】:

      • 如果除了Object.freeze() 的不可变属性之外,您还需要可变类属性,只需将它们包装到某个可变对象中即可。示例:代替class Cnt { static __cnt=0; get uniq() { return ++Cnt.__cnt } }; Object.freeze(Cnt)class Cnt { static __var={cnt:0}; get uniq() { return ++Cnt.__var.cnt } }; Object.freeze(Cnt)
      【解决方案11】:

      这是您可以做的另一种方法

      /*
      one more way of declaring constants in a class,
      Note - the constants have to be declared after the class is defined
      */
      class Auto{
         //other methods
      }
      Auto.CONSTANT1 = "const1";
      Auto.CONSTANT2 = "const2";
      
      console.log(Auto.CONSTANT1)
      console.log(Auto.CONSTANT2);

      注意 - 顺序很重要,你不能有上面的常量

      用法

      console.log(Auto.CONSTANT1);
      

      【讨论】:

      • 虽然它们不是一成不变的
      【解决方案12】:

      您可以使用import * as 语法。虽然不是一个类,但它们是真正的const 变量。

      Constants.js

      export const factor = 3;
      export const pi = 3.141592;
      

      index.js

      import * as Constants from 'Constants.js'
      console.log( Constants.factor );
      

      【讨论】:

        【解决方案13】:

        如果您喜欢在函数和类语法之间混合和匹配,您可以在类之后声明常量(常量被“提升”)。请注意,Visual Studio Code 将难以自动格式化混合语法(尽管它有效)。

        class MyClass {
            // ...
        
        }
        MyClass.prototype.consts = { 
            constant1:  33,
            constant2: 32
        };
        mc = new MyClass();
        console.log(mc.consts.constant2);    

        【讨论】:

          【解决方案14】:

          加上其他答案,您需要导出类以在不同的类中使用。这是它的打字稿版本。

          //Constants.tsx
          const DEBUG: boolean = true;
          
          export class Constants {
            static get DEBUG(): boolean {
              return DEBUG;
            }
          }
          
          //Anotherclass.tsx
          import { Constants } from "Constants";
          
          if (Constants.DEBUG) {
            console.log("debug mode")
          }

          【讨论】:

            【解决方案15】:

            我发现的最简洁的方法是使用 TypeScript - 请参阅 How to implement class constants?

            class MyClass {
                static readonly CONST1: string = "one";
                static readonly CONST2: string = "two";
                static readonly CONST3: string = "three";
            }
            

            【讨论】:

            • 抱歉,投了反对票,因为没有运行时保护。例如 MyClass['CO'+'NST1']='bug' 仍然会更改常量,即使在 Typescript 中也是如此readonly 只是编译时的糖,因为 Typescript 编译器不能神奇地从无到有地创建不可变的类属性。 因此,编译器既不保护它不了解的任何东西,也不保护运行时不被意外更改。 更糟糕的是:你可能认为你受到保护,但实际上并没有!(YMMV ,来自 Ubuntu 20.04 的测试 Typescript 编译器显然不使用 Object.freeze())
            【解决方案16】:

            给你!

            const Status = Object.freeze(class Status {
              static Disabled = 0
              static Live = 1
            })
            

            【讨论】:

              【解决方案17】:

              如果试图为一个类设置一个常量/变量静态;尝试使用哈希 (#) 来定义占位符,而不是使用函数来访问它。

              class Region {
                  // initially empty, not accessible from outside
                  static #empty_region = null; 
              
                  /* 
                      Make it visible to the outside and unchangeable 
                      [note] created on first call to getter.
                  */
              
                  static EMPTY() {
                      if (!this.#empty_region)
                          this.#empty_region = new Region(0, 0, 0, 0);
                      return this.#empty_region;
                  }
              
                  #reg = {x0:0, y0:0, x1:0, y1:0};
              
                  constructor(x0, y0, x1, y1) { 
                      this.setRegion(x0, y0, x1, y1);
                  }
              
                  // setters/getters
              }
              

              实施:

              let someRegion = Region.EMPTY();
              
              let anotherRegion = Region.EMPTY();
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2020-02-12
                • 1970-01-01
                相关资源
                最近更新 更多