javascript - ejemplos - Alternativas de clase de ES6 alternativas
javascript class properties (13)
Actualmente en ES5 muchos de nosotros estamos usando el siguiente patrón en marcos para crear clases y variables de clase, lo cual es cómodo:
// ES 5
FrameWork.Class({
variable: ''string'',
variable2: true,
init: function(){
},
addItem: function(){
}
});
En ES6 puede crear clases de forma nativa, pero no hay opción para tener variables de clase:
// ES6
class MyClass {
const MY_CONST = ''string''; // <-- this is not possible in ES6
constructor(){
this.MY_CONST;
}
}
Lamentablemente, lo anterior no funcionará, ya que las clases solo pueden contener métodos.
Entiendo que puedo esto. this.myVar = true
en el constructor
... pero no quiero "desechar" mi constructor, especialmente cuando tengo 20-30 + params para una clase más grande.
Estaba pensando en muchas maneras de manejar este problema, pero aún no he encontrado ninguna buena. (Por ejemplo: cree un controlador ClassConfig
y pase un objeto de parameter
, que se declara por separado de la clase. Luego, el controlador se adjuntaría a la clase. Estaba pensando en WeakMaps
también para integrarlo, de alguna manera).
¿Qué tipo de ideas tendrías para manejar esta situación?
¿Qué hay de la vieja escuela?
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);
En el constructor usted menciona solo aquellos vars que deben ser computados. Me gusta la herencia de prototipo para esta función, ya que puede ayudar a ahorrar mucha memoria (en caso de que haya muchas vars nunca asignadas).
¿Se puede evitar todo el problema utilizando literales fuertes y una pequeña biblioteca de plantillas que se ejecutan con lógica en un cierre más amplio?
ignorando el cierre por ahora
const myDynamicInputs=(items)=>/backtick -${ items.map((item, I, array)=>''${do tons of junk}'').join('''')}'';
http://codepen.io/jfrazz/pen/BQJPBZ/
ESTE es el ejemplo más simple que puedo ofrecer desde el repositorio. Las primeras 400 líneas son una biblioteca de datos + algunas funciones básicas de utilidad. Más un puñado de constantes de utilidad.
Después de la placa de la caldera, que estamos convirtiendo en un uri de datos, descargado por los usuarios de la aplicación, tenemos plantillas de matriz que deben levantarse y volver a implementarse, pero que pueden componerse de cualquier cosa, desde entradas, menús desplegables o 52 páginas. de preguntas y datos.
Este es el segundo ejemplo: Coma un objeto, obtenga entradas de varios tipos, todos utilizando const como la variable base de la biblioteca, que se está construyendo.
http://codepen.io/jfrazz/pen/rWprVR/
No es exactamente lo que pidió, pero una clara muestra de que la constante puede ser bastante dinámica.
Actualización 2018:
Ahora hay una propuesta para la etapa 3; espero que esta respuesta quede obsoleta en unos pocos meses.
Mientras tanto, cualquiera que use TypeScript o babel puede usar la sintaxis:
varName = value
Dentro de un cuerpo de declaración / expresión de clase definirá una variable. Esperemos que en unos meses / semanas pueda publicar una actualización.
Las notas en la wiki de ES para la propuesta en ES6 ( clases mínimas mínimas ) nota:
No existe (intencionalmente) una forma declarativa directa para definir propiedades de datos de prototipo (que no sean métodos) propiedades de clase o propiedad de instancia
Las propiedades de clase y las propiedades de datos prototipo deben crearse fuera de la declaración.
A las propiedades especificadas en una definición de clase se les asignan los mismos atributos que si aparecieran en un objeto literal.
Esto significa que lo que estás pidiendo fue considerado, y explícitamente decidido en contra.
¿pero por qué?
Buena pregunta. La buena gente de TC39 quiere que las declaraciones de clase declaren y definan las capacidades de una clase. No sus miembros. Una declaración de clase ES6 define su contrato para su usuario.
Recuerde, una definición de clase define los métodos de prototipo ; la definición de variables en el prototipo generalmente no es algo que usted hace. Usted puede, por supuesto, utilizar:
constructor(){
this.foo = bar
}
En el constructor como usted sugirió. Véase también el resumen del consenso .
ES7 y más allá
Se está trabajando en una nueva propuesta para ES7 que permite variables de instancia más concisas a través de declaraciones y expresiones de clase - https://esdiscuss.org/topic/es7-property-initializers
Bueno, puedes declarar variables dentro del Constructor.
class Foo {
constructor() {
var name = "foo"
this.method = function() {
return name
}
}
}
var foo = new Foo()
foo.method()
Como Benjamin dijo en su respuesta, TC39 decidió explícitamente no incluir esta función al menos para ES2015. Sin embargo, el consenso parece ser que lo agregarán en ES2016.
La sintaxis aún no se ha decidido, pero hay una https://esdiscuss.org/topic/es7-property-initializers para ES2016 que le permitirá declarar propiedades estáticas en una clase.
Gracias a la magia de Babel, puedes usar esto hoy. Habilite la transformación de las propiedades de la clase de acuerdo con estas instrucciones y estará listo. Aquí hay un ejemplo de la sintaxis:
class foo {
static myProp = ''bar''
someFunction() {
console.log(this.myProp)
}
}
Esta propuesta se encuentra en un estado muy temprano, así que prepárese para modificar su sintaxis a medida que pasa el tiempo.
En tu ejemplo:
class MyClass {
const MY_CONST = ''string'';
constructor(){
this.MY_CONST;
}
}
Debido a que MY_CONST es primitivo https://developer.mozilla.org/en-US/docs/Glossary/Primitive podemos simplemente hacer:
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
Pero si MY_CONST
es un tipo de referencia como static get MY_CONST() {return [''string''];}
resultado de alerta es string, false . En tal caso, el operador de delete
puede hacer el truco:
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
Y finalmente para la variable de clase no 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
Este es un combo de hacking un poco hackish y funciona para mí.
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;
}
}
usado en otros lugares
var conf = ConstantThingy.NO_REENTER__INIT;
if(conf.init)...
La forma en que resolví esto, que es otra opción (si tiene jQuery disponible), fue definir los campos en un objeto de la vieja escuela y luego extender la clase con ese objeto. Tampoco quería salpicar al constructor con tareas, esto parecía ser una solución ordenada.
function MyClassFields(){
this.createdAt = new Date();
}
MyClassFields.prototype = {
id : '''',
type : '''',
title : '''',
createdAt : null,
};
class MyClass {
constructor() {
$.extend(this,new MyClassFields());
}
};
- Actualización siguiendo el comentario de Bergi.
No JQuery Version:
class SavedSearch {
constructor() {
Object.assign(this,{
id : '''',
type : '''',
title : '''',
createdAt: new Date(),
});
}
}
Todavía terminas con un constructor ''gordo'', pero al menos es todo en una clase y asignado en un hit.
EDIT # 2: Ahora he completado el círculo y ahora estoy asignando valores en el constructor, por ejemplo
class SavedSearch {
constructor() {
this.id = '''';
this.type = '''';
this.title = '''';
this.createdAt = new Date();
}
}
¿Por qué? Realmente simple, utilizando lo anterior más algunos comentarios de JSdoc, PHPStorm pudo completar el código en las propiedades. Asignar todas las variables en un solo golpe fue agradable, pero la incapacidad de codificar las propiedades completa, imo, no vale la pena por el beneficio de rendimiento (casi sin duda minúsculo).
Puedes imitar el comportamiento de las clases de es6 ... y usar tus variables de clase :)
Mira mamá ... no hay clases!
// 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);
Lo puse en GitHub
Solo para agregar a la respuesta de Benjamin, las variables de clase son posibles, pero no usarías un prototype
para configurarlas.
Para una variable de clase verdadera, querría hacer algo como lo siguiente:
class MyClass {}
MyClass.foo = ''bar'';
Desde dentro de un método de clase se puede acceder a esta variable como this.constructor.foo
(o MyClass.foo
).
Estas propiedades de clase normalmente no serían accesibles desde la instancia de clase. es decir, MyClass.foo
da ''bar''
pero la new MyClass().foo
undefined
está undefined
Si también desea tener acceso a su variable de clase desde una instancia, tendrá que definir adicionalmente un captador:
class MyClass {
get foo() {
return this.constructor.foo;
}
}
MyClass.foo = ''bar'';
Solo probé esto con Traceur, pero creo que funcionará igual en una implementación estándar.
JavaScript realmente no tiene clases . Incluso con ES6, estamos viendo un lenguaje basado en prototipos o objetos en lugar de un lenguaje basado en clases. En cualquier function X () {}
, X.prototype.constructor
apunta de nuevo a X
Cuando el new
operador se utiliza en X
, se crea un nuevo objeto heredando X.prototype
. Cualquier propiedad indefinida en ese nuevo objeto (incluido el constructor
) se busca desde allí. Podemos pensar en esto como generando objetos y propiedades de clase.
Ya que su problema es principalmente estilístico (no querer llenar el constructor con un montón de declaraciones) también se puede resolver estilísticamente.
De la forma en que lo veo, muchos lenguajes basados en clases hacen que el constructor sea una función que lleva el nombre de la clase. Estilísticamente, podríamos usar eso para hacer una clase de ES6 que estilísticamente tenga sentido pero no agrupe las acciones típicas que tienen lugar en el constructor con todas las declaraciones de propiedades que estamos haciendo. Simplemente usamos el constructor JS real como el "área de declaración", luego hacemos una clase llamada función que de otra manera trataríamos como el área de "otro material del constructor", llamándola al final del verdadero constructor.
"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(); } }
Ambos serán llamados a medida que se construya la nueva instancia.
A Sorta le gusta tener 2 constructores donde se separan las declaraciones y las otras acciones del constructor que desea realizar, y estilísticamente hace que no sea tan difícil entender que eso es lo que está sucediendo también.
Me parece que es un estilo agradable para usar cuando se trata de muchas declaraciones y / o muchas acciones que deben suceder en la creación de instancias y que desean mantener las dos ideas distintas una de la otra.
NOTA : No uso muy a propósito las ideas idiomáticas típicas de "inicializar" (como un método init()
o initialize()
) porque a menudo se usan de manera diferente. Existe una especie de presunta diferencia entre la idea de construir e inicializar. Trabajando con constructores, las personas saben que se les llama automáticamente como parte de la creación de instancias. Al ver un método de init
, muchas personas asumirán sin una segunda mirada que necesitan hacer algo en la forma de var mc = MyClass(); mc.init();
var mc = MyClass(); mc.init();
, porque así es como normalmente se inicializa. No estoy tratando de agregar un proceso de inicialización para el usuario de la clase, estoy tratando de agregar al proceso de construcción de la clase en sí.
Si bien algunas personas pueden hacer una doble toma por un momento, ese es realmente el punto clave: les comunica que la intención es parte de la construcción, incluso si eso los hace hacer un poco de doble toma y vuelta "eso no es cómo funcionan los constructores de ES6 "y tómese un segundo mirando al constructor real para ir" oh, lo llaman en la parte inferior, veo ", eso es mucho mejor que NO comunicar esa intención (o comunicarla de manera incorrecta) y probablemente obtener una gran cantidad de la gente lo usa mal, tratando de inicializarlo desde afuera y basura. Eso es muy intencional para el patrón que sugiero.
Para aquellos que no quieren seguir ese patrón, el opuesto exacto también puede funcionar. Agrada las declaraciones a otra función al principio. Tal vez el nombre es "propiedades" o "publicProperties" o algo así. Luego ponga el resto de las cosas en el constructor normal.
"use strict"; class MyClass { properties() { this.prop1 = ''blah 1''; this.prop2 = ''blah 2''; this.prop3 = ''blah 3''; } constructor() { this.properties(); doWhatever(); } }
Tenga en cuenta que este segundo método puede parecer más limpio, pero también tiene un problema inherente en el que las properties
se anulan, ya que una clase que usa este método se extiende a otra. Tendrías que dar más nombres únicos a las properties
para evitar eso. Mi primer método no tiene este problema porque su mitad falsa del constructor tiene un nombre único después de la clase.
[Hilo largo, no estoy seguro si ya está listado como una opción ...].
Una alternativa simple sería definir la const fuera de clase. Esto será accesible solo desde el propio módulo, a menos que esté acompañado por un captador.
De esta manera el prototype
no está lleno de basura y obtienes la 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);
}
}