在ES6类中声明静态常量?


311

我想在中实现常量class,因为在代码中找到常量是很有意义的。

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

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

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

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


7
就我个人而言,我只是使用大写的VARNAMES,并告诉自己不要触摸它们;)
Twojr 2016年

3
@twicejr我认为这是不一样的,因为无需先实例化该类的对象就可以访问静态变量?
卢卡斯·摩根

Answers:


385

您可以执行以下操作:

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

export const constant1 = 33;

并在必要时从模块导入该文件。或者,基于您的静态方法思想,您可以声明一个static get访问器

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

  static get constant1() {
    return constant1;
  }

  static get constant2() {
    return constant2;
  }
}

这样,您将不需要括号:

const one = Example.constant1;

Babel REPL示例

然后,就像您说的那样,由于a 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;
}

但是不幸的是,此类属性语法仅在ES7提议中,即使这样,它也不允许添加const到属性中。


是否可以确认是否为此类事情计算过一次静态属性,还是使用IIFE并在IIFE中手动添加该属性以避免重复构造返回值是否更安全?我担心如果吸气剂的结果真的很重,例如100000条目JSObject,那么可怜的吸气剂每次调用吸气剂时都必须构造它。它可以很容易地通过performance.now/date diff进行测试,但是它的实现方式可能有所不同,它肯定更容易将getters作为文字评估而不是高级决策(无论其是否恒定)来实现。
德米特里(Dmitry)

3
尽管上面的方法巧妙地将常量属性添加到类中,但该常量的实际值在类定义“ {}”的“外部”,这确实违反了封装的定义之一。我猜仅在类内部定义一个常量属性就足够了,在这种情况下,不需要获取。
NoChance

1
@NoChance好点。那只是说明性的。如果没有必要,没有理由getter方法不能完全封装该值。
CodingIntrigue

期待使用ES7提案,因为它对我来说更自然,并且等同于大多数OO语言。
Sangimed

我要声明一个常量的实例变量是什么?我可以这样做this.defineProperty(this, 'constant1', {...})
弗朗西斯Boi的

33
class Whatever {
    static get MyConst() { return 10; }
}

let a = Whatever.MyConst;

似乎为我工作。


这可以在类中以常规方法访问吗?
PirateApp '19

3
@PirateApp您可以作为静态方法在任何地方访问它,甚至可以从类的实例内部访问它。但是,由于它是静态的,所以您不能this.MyConstWhatever实例内部使用它,因此始终必须这样编写: Whatever.MyConst
TheDarkIn1978 '19

23

我正在使用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


21
问题在于常量是可重新分配的。
运营商

3
仅供参考,现在是通天stage-2

3
这些不是常数
Dave L.

1
@CodingIntrigue可以Object.freeze()在类上调用吗?

1
@锑我还没有测试过,但我会这样认为。问题在于它将应用于类的所有属性。也是非静态的。
CodingIntrigue

14

此文件中声明:

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

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

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

constructor(){
    this.key = value
}

2
是的,这可以工作。另外,我想提到的是,构造函数在创建实例时调用,并且对于每个实例,this.key都不相同。静态方法和属性使我们可以直接从类中使用它们,而无需创建实例。静态方法/属性有优缺点。
Kirill Gusyatin

1
常量应该是不变的。在构造期间分配给对象的属性将产生可以修改的属性。
philraj

11

也可以Object.freeze在您的class(es6)/ constructor function(es5)对象上使用以使其不可变:

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

尝试更改该类将给您一个软失败(不会引发任何错误,它只会无效)。


3
对于那些来自其他语言的人来说,这种软失败非常可怕-只是适应了这样的想法,即工具对发现错误没有太大帮助,现在即使运行时也无济于事。(否则,我喜欢您的解决方案。)
Tom

我喜欢Object.freeze()强制不变性,并且最近一直在使用它。只是不要忘记递归地应用它!
jeffwtribble

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”。
PokerFace

4

就像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;
}

1

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

/*
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);


5
它们并不是一成不变的
John Harding

1

您可以创建一种使用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);
  }
}

1

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

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";

0

如果您愿意在函数和类语法之间进行混合和匹配,可以在类之后声明常量(常量被“提升”)。请注意,Visual Studio Code将难以自动设置混合语法的格式(尽管它可以工作)。

class MyClass {
    // ...

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


0

我做到了

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

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


0

您可以这样定义它:

class Foo {
  static MyConst = 200;

  myFunc() {
    const doubleConst = Foo.MyConst * 2;
  }
}

0

您可以使用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 );
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.