win*_*ter 477 javascript class ecmascript-6
目前在ES5中,我们很多人在框架中使用以下模式来创建类和类变量,这很舒服:
// ES 5
FrameWork.Class({
variable: 'string',
variable2: true,
init: function(){
},
addItem: function(){
}
});
Run Code Online (Sandbox Code Playgroud)
在ES6中,您可以本机创建类,但没有选项可以使用类变量:
// ES6
class MyClass {
const MY_CONST = 'string'; // <-- this is not possible in ES6
constructor(){
this.MY_CONST;
}
}
Run Code Online (Sandbox Code Playgroud)
遗憾的是,上面的代码不起作用,因为类只能包含方法.
我知道我可以this.myVar = true
在constructor
...但我不想"垃圾"我的构造,特别是当我有一个更大的类20-30 +参数.
我正在考虑处理这个问题的许多方法,但还没有找到任何好的方法.(例如:创建一个ClassConfig
处理程序,并传递一个parameter
与该类分开声明的对象.然后处理程序将附加到该类.我正在考虑WeakMaps
以某种方式集成.)
你有什么样的想法来处理这种情况?
Ben*_*aum 497
2018年更新:
现在有一个第3阶段的提案 - 我期待在几个月内让这个答案过时.
在此期间,任何使用TypeScript或babel的人都可以使用以下语法:
varName = value
Run Code Online (Sandbox Code Playgroud)
在类声明/表达式主体内部,它将定义一个变量.希望在几个月/几周后,我将能够发布更新.
ES维基中关于ES6中提案的注释(最小最小类)注意:
(故意)没有直接声明方式来定义原型数据属性(方法除外)类属性或实例属性
需要在声明之外创建类属性和原型数据属性.
在类定义中指定的属性被赋予与它们出现在对象文字中相同的属性.
这意味着您要求的内容被考虑,并明确决定反对.
好问题.TC39的优秀人员希望类声明声明和定义类的功能.不是它的成员.ES6类声明定义了其用户的合同.
请记住,类定义定义了原型方法 - 在原型上定义变量通常不是你做的事情.你当然可以使用:
constructor(){
this.foo = bar
}
Run Code Online (Sandbox Code Playgroud)
在像你建议的构造函数中.另见共识摘要.
正在开发一个关于ES7的新提议,它允许通过类声明和表达式提供更简洁的实例变量 - https://esdiscuss.org/topic/es7-property-initializers
lys*_*ing 126
只是为了添加Benjamin的答案 - 类变量是可能的,但你不会prototype
用来设置它们.
对于真正的类变量,您需要执行以下操作:
class MyClass {}
MyClass.foo = 'bar';
Run Code Online (Sandbox Code Playgroud)
从类方法中可以将变量作为this.constructor.foo
(或MyClass.foo
)访问.
通常无法从类实例访问这些类属性.即MyClass.foo
给人'bar'
但new MyClass().foo
为undefined
如果您还想从实例访问您的类变量,则还必须另外定义一个getter:
class MyClass {
get foo() {
return this.constructor.foo;
}
}
MyClass.foo = 'bar';
Run Code Online (Sandbox Code Playgroud)
我只用Traceur对它进行了测试,但我相信它在标准实现中也会有相同的效果.
JavaScript实际上没有类.即使使用ES6,我们也在寻找基于对象或原型的语言而不是基于类的语言.在任何function X () {}
,X.prototype.constructor
回到点X
.使用new
运算符时X
,将创建一个继承的新对象X.prototype
.从那里查找该新对象(包括)中的任何未定义属性constructor
.我们可以将其视为生成对象和类属性.
Kos*_*ika 25
class Foo {
bar = 2
static iha = 'string'
}
const foo = new Foo();
console.log(foo.bar, foo.iha, Foo.bar, Foo.iha);
// 2, undefined, undefined, 'string'
Run Code Online (Sandbox Code Playgroud)
Ole*_*zko 24
在你的例子中:
class MyClass {
const MY_CONST = 'string';
constructor(){
this.MY_CONST;
}
}
Run Code Online (Sandbox Code Playgroud)
由于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
Run Code Online (Sandbox Code Playgroud)
但是如果MY_CONST
是类似static get MY_CONST() {return ['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
Run Code Online (Sandbox Code Playgroud)
最后对于类变量不是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
Run Code Online (Sandbox Code Playgroud)
Jim*_*nny 17
由于您的问题主要是风格(不想用一堆声明来填充构造函数),因此它也可以在风格上得到解决.
我查看它的方式,许多基于类的语言使构造函数成为以类名本身命名的函数.在风格上我们可以使用它来创建一个风格上仍然有意义的ES6类,但不会将构造函数中发生的典型操作与我们正在进行的所有属性声明分组.我们只是使用实际的JS构造函数作为"声明区域",然后创建一个名为function的类,否则我们将其视为"其他构造函数"区域,在真正的构造函数的末尾调用它.
"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就像有两个构造函数,你可以将声明和你想要采取的其他构造函数分开,并且在风格上让人不难理解这也是正在发生的事情.
我发现在处理大量声明和/或需要在实例化时发生的许多动作并且希望保持这两个想法彼此不同时,使用它是一种很好的风格.
注意:我非常有目的地不使用"初始化"(例如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
以避免这种情况.我的第一个方法没有这个问题,因为它的假构造函数的一半是在类之后唯一命名的.
zar*_*one 14
那些oldschool的方式呢?
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);
Run Code Online (Sandbox Code Playgroud)
在构造函数中,您只提到那些必须计算的变量.我喜欢这个功能的原型继承 - 它可以帮助节省大量内存(如果有很多未分配的变量).
Bon*_*Oak 13
正如本杰明在回答中所说,TC39明确决定不至少在ES2015中包含此功能.然而,共识似乎是他们将在ES2016中添加它.
语法尚未确定,但ES2016 的初步建议允许您在类上声明静态属性.
感谢babel的魔力,你今天可以使用它.根据这些说明启用类属性转换,你很高兴.这是一个语法示例:
class foo {
static myProp = 'bar'
someFunction() {
console.log(this.myProp)
}
}
Run Code Online (Sandbox Code Playgroud)
此提案处于非常早期的状态,因此随着时间的推移,请准备好调整语法.
Wil*_*een 10
ES7
类成员语法:ES7
有一个“垃圾化”构造函数的解决方案。下面是一个例子:
class Car {
wheels = 4;
weight = 100;
}
const car = new Car();
console.log(car.wheels, car.weight);
Run Code Online (Sandbox Code Playgroud)
上面的示例将如下所示ES6
:
class Car {
constructor() {
this.wheels = 4;
this.weight = 100;
}
}
const car = new Car();
console.log(car.wheels, car.weight);
Run Code Online (Sandbox Code Playgroud)
使用此语法时请注意,并非所有浏览器都支持此语法,并且可能必须将其转译为早期版本的 JS。
function generateCar(wheels, weight) {
class Car {
constructor() {}
wheels = wheels;
weight = weight;
}
return new Car();
}
const car1 = generateCar(4, 50);
const car2 = generateCar(6, 100);
console.log(car1.wheels, car1.weight);
console.log(car2.wheels, car2.weight);
Run Code Online (Sandbox Code Playgroud)
[长线程,不确定它是否已作为选项列出...].
一个简单的替代方案是在类外定义const.除非附带吸气剂,否则只能从模块本身访问.
这种方式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);
}
}
Run Code Online (Sandbox Code Playgroud)
你可以模仿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);
Run Code Online (Sandbox Code Playgroud)
我把它放在GitHub上