目前在 ES5 中,我们中的许多人在框架中使用以下模式来创建类和类变量,这很舒服:
// ES 5
FrameWork.Class({
variable: 'string',
variable2: true,
init: function(){
},
addItem: function(){
}
});
在 ES6 中,您可以原生创建类,但没有选项可以使用类变量:
// ES6
class MyClass {
const MY_CONST = 'string'; // <-- this is not possible in ES6
constructor(){
this.MY_CONST;
}
}
可悲的是,上述方法不起作用,因为类只能包含方法。
我知道我可以在 constructor
中this.myVar = true
……但我不想“垃圾”我的构造函数,尤其是当我有 20-30 多个参数用于更大的类时。
我想了很多方法来处理这个问题,但还没有找到任何好的方法。 (例如:创建一个 ClassConfig
处理程序,并传递一个 parameter
对象,该对象与类分开声明。然后处理程序将附加到类。我正在考虑 WeakMaps
也以某种方式集成。)
你有什么样的想法来处理这种情况?
this.member = member
?
public variable2 = true
吗?这将在原型上定义它。
2018年更新:
现在有一个第 3 阶段的提案——我期待在几个月内让这个答案过时。
与此同时,任何使用 TypeScript 或 babel 的人都可以使用以下语法:
varName = value
在类声明/表达式主体内,它将定义一个变量。希望在几个月/几周后,我能够发布更新。
更新:Chrome 74 现在可以使用此语法。
ES wiki 中关于 ES6 (maximally minimal classes) 提案的注释说明:
(故意)没有直接的声明方式来定义原型数据属性(除了方法)类属性,或者实例属性类属性和原型数据属性需要在声明之外创建。类定义中指定的属性被分配了相同的属性,就好像它们出现在对象字面量中一样。
这意味着您所要求的内容已被考虑并明确决定反对。
但为什么?
好问题。 TC39 的好人希望类声明能够声明和定义类的功能。不是它的成员。一个 ES6 类声明为它的用户定义了它的契约。
请记住,类定义定义了原型方法——在原型上定义变量通常不是您要做的事情。您当然可以使用:
constructor(){
this.foo = bar
}
在你建议的构造函数中。另见the summary of the consensus。
ES7 及更高版本
正在制定 ES7 的新提案,通过类声明和表达式允许更简洁的实例变量 - https://esdiscuss.org/topic/es7-property-initializers
只是为了补充本杰明的答案-类变量是可能的,但您不会使用 prototype
来设置它们。
对于真正的类变量,您需要执行以下操作:
class MyClass {}
MyClass.foo = 'bar';
在类方法中,该变量可以作为 this.constructor.foo
(或 MyClass.foo
)访问。
这些类属性通常不能从类实例访问。即 MyClass.foo
给出 'bar'
但 new MyClass().foo
是 undefined
如果您还想从实例访问您的类变量,则必须另外定义一个 getter:
class MyClass {
get foo() {
return this.constructor.foo;
}
}
MyClass.foo = 'bar';
我只用 Traceur 对此进行了测试,但我相信它在标准实现中也能正常工作。
JavaScript doesn't really have classes。即使使用 ES6,我们也正在研究基于对象或原型的语言,而不是基于类的语言。在任何 function X () {}
中,X.prototype.constructor
都指向 X
。在 X
上使用 new
运算符时,将创建一个继承 X.prototype
的新对象。从那里查找该新对象(包括 constructor
)中的任何 undefined 属性。我们可以将其视为生成对象和类属性。
MyClass.foo = 'bar';
时。它不正是在做类限制应该阻止我们做的事情——即在原型上放置变量吗?
在您的示例中:
class MyClass {
const MY_CONST = 'string';
constructor(){
this.MY_CONST;
}
}
由于 MY_CONST 是原始的 https://developer.mozilla.org/en-US/docs/Glossary/Primitive 我们可以这样做:
class MyClass {
static get MY_CONST() {
return 'string';
}
get MY_CONST() {
return this.constructor.MY_CONST;
}
constructor() {
alert(this.MY_CONST === this.constructor.MY_CONST);
}
}
alert(MyClass.MY_CONST);
new MyClass
// alert: string ; true
但如果 MY_CONST
是引用类型,例如 static get MY_CONST() {return ['string'];}
,则警报输出为 string,false。在这种情况下 delete
运算符可以做到这一点:
class MyClass {
static get MY_CONST() {
delete MyClass.MY_CONST;
return MyClass.MY_CONST = 'string';
}
get MY_CONST() {
return this.constructor.MY_CONST;
}
constructor() {
alert(this.MY_CONST === this.constructor.MY_CONST);
}
}
alert(MyClass.MY_CONST);
new MyClass
// alert: string ; true
最后对于非 const
的类变量:
class MyClass {
static get MY_CONST() {
delete MyClass.MY_CONST;
return MyClass.MY_CONST = 'string';
}
static set U_YIN_YANG(value) {
delete MyClass.MY_CONST;
MyClass.MY_CONST = value;
}
get MY_CONST() {
return this.constructor.MY_CONST;
}
set MY_CONST(value) {
this.constructor.MY_CONST = value;
}
constructor() {
alert(this.MY_CONST === this.constructor.MY_CONST);
}
}
alert(MyClass.MY_CONST);
new MyClass
// alert: string, true
MyClass.MY_CONST = ['string, 42']
alert(MyClass.MY_CONST);
new MyClass
// alert: string, 42 ; true
delete
运算符(如果单独使用)。您真正想要的是Object.defineProperty
。
由于您的问题主要是风格上的(不想用一堆声明填充构造函数),它也可以在风格上解决。
在我看来,许多基于类的语言的构造函数都是以类名本身命名的函数。从风格上讲,我们可以使用它来制作一个风格上仍然有意义的 ES6 类,但不会将构造函数中发生的典型动作与我们正在做的所有属性声明组合在一起。我们只需将实际的 JS 构造函数用作“声明区”,然后创建一个名为函数的类,否则我们会将其视为“其他构造函数”区域,并在真正的构造函数的末尾调用它。
"use strict"; class MyClass { // only declare your properties and then call this.ClassName(); from here constructor(){ this.prop1 = 'blah 1'; this.prop2 = 'blah 2'; this.prop3 = 'blah 3'; this.MyClass(); } // all sorts of other "constructor" stuff, no longer jumbled with declarations MyClass() { doWhatever(); } }
两者都将在构造新实例时被调用。
Sorta 就像拥有 2 个构造函数,您可以在其中将声明和您想要执行的其他构造函数操作分开,并且从风格上来说,理解这也是正在发生的事情并不难。
我发现在处理大量声明和/或需要在实例化时发生的大量动作并希望使这两个想法彼此不同时使用它是一种不错的风格。
注意:我非常有目的地不使用“初始化”的典型惯用思想(如 init()
或 initialize()
方法),因为它们的用法通常不同。构造和初始化的想法之间存在一种假定的差异。使用构造函数的人都知道,它们是作为实例化的一部分自动调用的。看到 init
方法,许多人会不假思索地认为他们需要按照 var mc = MyClass(); mc.init();
的形式执行某些操作,因为这就是您通常初始化的方式。我不是要为类的用户添加初始化过程,而是要添加到类本身的构造过程。
虽然有些人可能会做一时的双重考虑,但这实际上是重点:它向他们传达意图是构建的一部分,即使这让他们做了一些双重考虑并去“那不是ES6 构造函数是如何工作的”,然后再看一下实际的构造函数去“哦,他们在底部调用它,我明白了”,这比不传达那个意图(或错误传达它)要好得多,而且可能会得到很多人们使用它错误,试图从外部初始化它和垃圾。这对我建议的模式非常有意。
对于那些不想遵循这种模式的人来说,完全相反的方法也可以。在开始时将声明移植到另一个函数。也许将其命名为“properties”或“publicProperties”之类的。然后把剩下的东西放在普通的构造函数中。
"use strict"; class MyClass { properties() { this.prop1 = 'blah 1'; this.prop2 = 'blah 2'; this.prop3 = 'blah 3'; } constructor() { this.properties(); doWhatever(); } }
请注意,第二种方法可能看起来更简洁,但它也存在一个固有问题,即 properties
被覆盖,因为使用此方法的一个类扩展了另一个类。您必须为 properties
提供更多唯一名称以避免这种情况。我的第一个方法没有这个问题,因为它的构造函数的假一半是唯一以类命名的。
init
。
init
是一种经常使用的模式,并且通常意味着当外部用户想要初始化时从类外部调用,即 var b = new Thing(); b.init();
。这是一个 100% 的风格选择,我更愿意传达它是通过利用其他语言中发现的模式自动调用的第二个函数。不太可能有人会看到这个并假设他们需要从外部调用 MyClass
方法,更有可能他们会意识到意图是在构造中起作用的第二个方法(即在实例化时由其自身调用)。
MyClass
。
$myvar
引用旨在保存 jQuery 对象的变量的标准方式与许多 PHP 程序员习惯的在变量开头使用 $ 的模式并不方便。只是那个小小的暗示“是的,这不是完全相同的东西......但是看......它仍然是一个变量,因为这是在某些语言中完成变量的一种方式!”有帮助。
properties()
函数的最后一个选项。但是,在扩展类时事情会变得有点复杂,所以我建议如果您在所有类中使用 properties()
函数来声明类属性,则在方法名称前加上类名(IE:MyClassProperties()
为了避免意外覆盖子类中的函数调用。另外,请记住,对 super()
的任何调用都必须首先在类构造函数中声明。
正如 Benjamin 在他的回答中所说,TC39 明确决定至少在 ES2015 中不包含此功能。然而,共识似乎是他们将在 ES2016 中添加它。
语法尚未确定,但 ES2016 有一个 preliminary proposal,可让您在类上声明静态属性。
感谢 babel 的魔力,你今天可以使用它。根据 these instructions 启用类属性转换,一切顺利。下面是一个语法示例:
class foo {
static myProp = 'bar'
someFunction() {
console.log(this.myProp)
}
}
该提案处于非常早期的状态,因此请准备好随着时间的推移调整您的语法。
老派的方式呢?
class MyClass {
constructor(count){
this.countVar = 1 + count;
}
}
MyClass.prototype.foo = "foo";
MyClass.prototype.countVar = 0;
// ...
var o1 = new MyClass(2); o2 = new MyClass(3);
o1.foo = "newFoo";
console.log( o1.foo,o2.foo);
console.log( o1.countVar,o2.countVar);
在构造函数中,您只提到那些必须计算的变量。我喜欢这个特性的原型继承——它可以帮助节省大量内存(如果有很多从未分配过的变量)。
[长线程,不确定它是否已经列为一个选项...]。
contsants only 的简单 alternative 将在类之外定义 const .这只能从模块本身访问,除非带有 getter。
这样 prototype
不会乱扔垃圾,您会得到 const
。
// will be accessible only from the module itself
const MY_CONST = 'string';
class MyClass {
// optional, if external access is desired
static get MY_CONST(){return MY_CONST;}
// access example
static someMethod(){
console.log(MY_CONST);
}
}
var
而不是 const
2) 使用此类的多个实例,会发生什么情况?然后外部变量将随着类的每个实例而改变
ES7 类成员语法:
ES7
有一个“垃圾”您的构造函数的解决方案。这是一个例子:
类汽车 { 车轮 = 4;重量 = 100; } const car = new Car(); console.log(car.wheels, car.weight);
上面的示例在 ES6
中将如下所示:
类汽车 { 构造函数() { this.wheels = 4; this.weight = 100; } } const car = new Car(); console.log(car.wheels, car.weight);
请注意,使用此语法时,可能并非所有浏览器都支持此语法,并且可能必须将其转换为早期版本的 JS。
奖励:对象工厂:
函数 generateCar(wheels, weight) { class Car { constructor() {} Wheels = Wheels;重量=重量; } 返回新车(); } const car1 = generateCar(4, 50); const car2 = generateCar(6, 100); console.log(car1.wheels, car1.weight); console.log(car2.wheels, car2.weight);
您可以模仿 es6 类的行为......并使用您的类变量:)
妈妈看……没有课!
// Helper
const $constructor = Symbol();
const $extends = (parent, child) =>
Object.assign(Object.create(parent), child);
const $new = (object, ...args) => {
let instance = Object.create(object);
instance[$constructor].call(instance, ...args);
return instance;
}
const $super = (parent, context, ...args) => {
parent[$constructor].call(context, ...args)
}
// class
var Foo = {
classVariable: true,
// constructor
[$constructor](who){
this.me = who;
this.species = 'fufel';
},
// methods
identify(){
return 'I am ' + this.me;
}
}
// class extends Foo
var Bar = $extends(Foo, {
// constructor
[$constructor](who){
$super(Foo, this, who);
this.subtype = 'barashek';
},
// methods
speak(){
console.log('Hello, ' + this.identify());
},
bark(num){
console.log('Woof');
}
});
var a1 = $new(Foo, 'a1');
var b1 = $new(Bar, 'b1');
console.log(a1, b1);
console.log('b1.classVariable', b1.classVariable);
我把它放在GitHub上
您仍然不能像在其他编程语言中那样声明任何类。但是您可以创建尽可能多的类变量。但问题是类对象的范围。所以据我说,在 ES6 Javascript 中进行 OOP 编程的最佳方式:-
class foo{
constructor(){
//decalre your all variables
this.MY_CONST = 3.14;
this.x = 5;
this.y = 7;
// or call another method to declare more variables outside from constructor.
// now create method level object reference and public level property
this.MySelf = this;
// you can also use var modifier rather than property but that is not working good
let self = this.MySelf;
//code .........
}
set MySelf(v){
this.mySelf = v;
}
get MySelf(v){
return this.mySelf;
}
myMethod(cd){
// now use as object reference it in any method of class
let self = this.MySelf;
// now use self as object reference in code
}
}
如果它只是造成 constructor
中问题的杂乱无章,为什么不实施一个 intializes
变量的 initialize
方法。当构造函数装满不必要的东西时,这是很正常的事情。即使在像 C#
这样的类型化程序语言中,通常也会添加一个 Initialize
方法来处理它。
我解决这个问题的方法是另一种选择(如果您有可用的 jQuery),是在老式对象中定义字段,然后使用该对象扩展类。我也不想给构造函数加上赋值,这似乎是一个很好的解决方案。
function MyClassFields(){
this.createdAt = new Date();
}
MyClassFields.prototype = {
id : '',
type : '',
title : '',
createdAt : null,
};
class MyClass {
constructor() {
$.extend(this,new MyClassFields());
}
};
- 更新以下 Bergi 的评论。
没有 JQuery 版本:
class SavedSearch {
constructor() {
Object.assign(this,{
id : '',
type : '',
title : '',
createdAt: new Date(),
});
}
}
您最终仍然会使用“胖”构造函数,但至少它全部在一个类中并一键分配。
编辑#2:我现在已经完成了一个完整的循环,现在正在构造函数中分配值,例如
class SavedSearch {
constructor() {
this.id = '';
this.type = '';
this.title = '';
this.createdAt = new Date();
}
}
为什么?真的很简单,使用上面加上一些 JSdoc 注释,PHPStorm 能够对属性执行代码完成。一次性分配所有变量很好,但是无法编写完整的属性,imo,不值得(几乎可以肯定是微不足道的)性能优势。
class
语法,那么您也可以使用 Object.assign
。不需要 jQuery。
MyClassFields
设为构造函数没有任何意义——它没有任何方法。你能详细说明你为什么这样做(而不是一个简单的返回对象文字的工厂函数)吗?
好吧,您可以在构造函数中声明变量。
class Foo {
constructor() {
var name = "foo"
this.method = function() {
return name
}
}
}
var foo = new Foo()
foo.method()
只需定义一个 getter。
类 MyClass { get MY_CONST () { return 'string'; } 构造函数 () { console.log ("MyClass MY_CONST:", this.MY_CONST); } } var obj = new MyClass();
截至 2021 年的最新浏览器(不是 IE,请参阅 MDN 浏览器图表)实现了 Public class fields,这似乎是您正在寻找的:
类 MyClass { 静态 foo = 3; } console.log(MyClass.foo);
但显然不可能将其设为 const
:Declaring static constants in ES6 classes?
静态吸气剂看起来非常接近:
类 MyClass { 静态获取 CONST() { 返回 3; } } MyClass.CONST = 4; // 属性不受影响 console.log(MyClass.CONST);
这是一个有点骇人听闻的静态组合,并为我工作
class ConstantThingy{
static get NO_REENTER__INIT() {
if(ConstantThingy._NO_REENTER__INIT== null){
ConstantThingy._NO_REENTER__INIT = new ConstantThingy(false,true);
}
return ConstantThingy._NO_REENTER__INIT;
}
}
在别处使用
var conf = ConstantThingy.NO_REENTER__INIT;
if(conf.init)...
singleton_instance
作为属性名称,以便每个人都了解您在此处所做的事情。
static
个属性static
也仅适用于方法。