javascript - props - node js class variables




Alternativas de classe de classe ES6 (10)

Atualmente no ES5 muitos de nós estão usando o seguinte padrão em frameworks para criar classes e variáveis ​​de classe, o que é confortável:

// ES 5
FrameWork.Class({

    variable: 'string',
    variable2: true,

    init: function(){

    },

    addItem: function(){

    }

});

No ES6 você pode criar classes nativamente, mas não há opção para ter variáveis ​​de classe:

// ES6
class MyClass {
    const MY_CONST = 'string'; // <-- this is not possible in ES6
    constructor(){
        this.MY_CONST;
    }
}

Infelizmente, o acima não funcionará, pois as classes só podem conter métodos.

Eu entendo que eu posso this.myVar = true no constructor ... mas eu não quero 'lixo' meu construtor, especialmente quando eu tenho 20-30 + params para uma classe maior.

Eu estava pensando em muitas maneiras de lidar com esse problema, mas ainda não encontrei nenhuma boa. (Por exemplo: crie um manipulador ClassConfig e passe um objeto de parameter , que é declarado separadamente da classe. Então o manipulador seria anexado à classe. Eu estava pensando no WeakMaps também para integrar, de alguma forma.)

Que tipo de ideias você teria para lidar com essa situação?


ES7 membro da classe ES7 :

ES7 tem uma solução para 'junking' sua função de construtor. Aqui está um exemplo:

class Car {
  
  wheels = 4;
  weight = 100;

}

const car = new Car();
console.log(car.wheels, car.weight);

O exemplo acima seria o seguinte no ES6 :

class Car {

  constructor() {
    this.wheels = 4;
    this.weight = 100;
  }

}

const car = new Car();
console.log(car.wheels, car.weight);

Esteja ciente, ao usar isso, de que essa sintaxe pode não ser suportada por todos os navegadores e pode ter que ser transpilada em uma versão anterior do JS.

Bônus: uma fábrica de objetos:

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


A maneira como resolvi isso, que é outra opção (se você tiver o jQuery disponível), foi Definir os campos em um objeto old-school e estender a classe com esse objeto. Eu também não queria apimentar o construtor com atribuições, isso parecia ser uma solução limpa.

function MyClassFields(){
    this.createdAt = new Date();
}

MyClassFields.prototype = {
    id : '',
    type : '',
    title : '',
    createdAt : null,
};

class MyClass {
    constructor() {
        $.extend(this,new MyClassFields());
    }
};

- Atualização Após o comentário de Bergi.

Nenhuma versão do JQuery:

class SavedSearch  {
    constructor() {
        Object.assign(this,{
            id : '',
            type : '',
            title : '',
            createdAt: new Date(),
        });

    }
}

Você ainda acaba com o construtor 'fat', mas pelo menos é tudo em uma classe e é atribuído em um hit.

EDIT # 2: Eu agora fiz um círculo completo e agora estou atribuindo valores no construtor, por exemplo

class SavedSearch  {
    constructor() {
        this.id = '';
        this.type = '';
        this.title = '';
        this.createdAt = new Date();
    }
}

Por quê? Simples, usando os comentários acima e alguns comentários do JSdoc, o PHPStorm pôde realizar a conclusão de código nas propriedades. Atribuir todas as vars em um hit foi bom, mas a incapacidade de codificar as propriedades, imo, não vale o benefício de desempenho (quase certamente minúsculo).


Atualização 2018:

Existe agora uma proposta de estágio 3 - estou ansioso para tornar esta resposta obsoleta em poucos meses.

Enquanto isso, qualquer pessoa usando o TypeScript ou o babel pode usar a sintaxe:

varName = value

Dentro de um corpo de declaração / expressão de classe e irá definir uma variável. Espero que daqui a alguns meses / semanas eu possa postar uma atualização.

As notas no ES wiki para a proposta em ES6 ( maximally minimal classes ) note:

Não há (intencionalmente) nenhuma maneira declarativa direta de definir propriedades de propriedades de dados de protótipos (que não sejam métodos) ou propriedades de instâncias

Propriedades de classe e propriedades de dados de protótipo precisam ser criadas fora da declaração.

As propriedades especificadas em uma definição de classe recebem os mesmos atributos como se fossem exibidas em um literal de objeto.

Isso significa que o que você está pedindo foi considerado e decidiu explicitamente.

mas por que?

Boa pergunta. As pessoas boas do TC39 querem declarações de classe para declarar e definir as capacidades de uma classe. Não seus membros. Uma declaração de classe ES6 define seu contrato para seu usuário.

Lembre-se, uma definição de classe define métodos de protótipo - definir variáveis ​​no protótipo geralmente não é algo que você faz. Você pode, claro, usar:

constructor(){
    this.foo = bar
}

No construtor como você sugeriu. Veja também o resumo do consenso .

ES7 e além

Uma nova proposta para o ES7 está sendo trabalhada, permitindo variáveis ​​de instância mais concisas através de declarações e expressões de classe - https://esdiscuss.org/topic/es7-property-initializers


Bem, você pode declarar variáveis ​​dentro do construtor.

class Foo {
    constructor() {
        var name = "foo"
        this.method = function() {
            return name
        }
    }
}

var foo = new Foo()

foo.method()

Como seu problema é principalmente estilístico (não querer encher o construtor com várias declarações), ele também pode ser resolvido estilisticamente.

Do jeito que eu vejo, muitas linguagens baseadas em classes têm o construtor como uma função com o nome da classe em si. Estilisticamente podemos usar isso para fazer uma classe ES6 que estilisticamente ainda faz sentido, mas não agrupa as ações típicas que ocorrem no construtor com todas as declarações de propriedade que estamos fazendo. Nós simplesmente usamos o construtor JS real como a "área de declaração", então fazemos uma classe chamada function que de outra forma tratamos como a área "other constructor stuff", chamando-a no final do verdadeiro construtor.

"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ão chamados quando a nova instância for construída.

Sorta gosta de ter 2 construtores onde você separa as declarações e as outras ações do construtor que você quer tomar, e estilisticamente não torna muito difícil de entender que é o que está acontecendo também.

Eu acho que é um bom estilo para usar quando se lida com muitas declarações e / ou muitas ações que precisam acontecer na instanciação e quer manter as duas idéias distintas uma da outra.

OBSERVAÇÃO : Eu propositalmente não uso as idéias idiomáticas típicas de "inicialização" (como um método init() ou initialize() ) porque elas são usadas frequentemente de forma diferente. Existe uma espécie de diferença presumida entre a ideia de construir e inicializar. Trabalhando com construtores, as pessoas sabem que são chamadas automaticamente como parte da instanciação. Vendo um método init muitas pessoas vão assumir sem um segundo olhar que eles precisam estar fazendo algo ao longo do formulário de var mc = MyClass(); mc.init(); var mc = MyClass(); mc.init(); , porque é assim que você normalmente inicializa. Eu não estou tentando adicionar um processo de inicialização para o usuário da classe, estou tentando adicionar ao processo de construção da própria classe.

Enquanto algumas pessoas podem pensar duas vezes por um momento, isso é realmente o ponto: ele comunica a elas que a intenção é parte da construção, mesmo que isso faça com que elas façam um pouco de um take e vão "isso não é como os construtores do ES6 trabalham "e olhe um segundo para o construtor atual" oh, eles o chamam na parte inferior, eu vejo ", isso é muito melhor do que NÃO comunicar essa intenção (ou comunicar incorretamente) e provavelmente obter muita pessoas usando errado, tentando inicializá-lo do lado de fora e lixo. Isso é muito intencional para o padrão que eu sugiro.

Para aqueles que não querem seguir esse padrão, o exato oposto também pode funcionar. Agrupa as declarações para outra função no começo. Talvez nomeie como "propriedades" ou "publicProperties" ou algo assim. Em seguida, coloque o resto das coisas no construtor normal.

"use strict";

class MyClass
{
    properties() {
        this.prop1 = 'blah 1';
        this.prop2 = 'blah 2';
        this.prop3 = 'blah 3';
    }

    constructor() {
        this.properties();
        doWhatever();
    }
}

Observe que esse segundo método pode parecer mais limpo, mas também tem um problema inerente em que as properties são substituídas, já que uma classe usando esse método estende outra. Você teria que dar nomes mais exclusivos às properties para evitar isso. Meu primeiro método não tem esse problema porque sua metade falsa do construtor é nomeada exclusivamente após a classe.


E o caminho da escola antiga?

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

No construtor, você menciona apenas as variáveis ​​que precisam ser calculadas. Eu gosto de protótipo de herança para este recurso - ele pode ajudar a economizar muita memória (no caso, se houver um monte de vars nunca atribuídos).


No seu exemplo:

class MyClass {
    const MY_CONST = 'string';
    constructor(){
        this.MY_CONST;
    }
}

Devido a que o MY_CONST é primitivo, https://developer.mozilla.org/en-US/docs/Glossary/Primitive , podemos apenas fazer:

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

Mas se MY_CONST for tipo de referência como static get MY_CONST() {return ['string'];} saída de alerta é string, false . Nesse caso, o operador delete pode fazer o truque:

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

E finalmente para a variável de classe não 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

Você pode evitar todo o problema usando literais fortes e uma pequena biblioteca de execução de lógica de modelos contida em um fechamento maior?

ignorando o fechamento por enquanto

const myDynamicInputs=(items)=>\backtick -${ items.map((item, I, array)=>'${do tons of junk}').join('')}';

http://codepen.io/jfrazz/pen/BQJPBZ/

ESTE é o exemplo mais simples que posso oferecer do repositório, as primeiras 400 linhas são uma biblioteca de dados + algumas funções básicas de utilitário. Mais um punhado de constantes de utilidade.

Após a placa do boiler, que estamos transformando em dados uri - baixados por usuários do aplicativo -, temos modelos de matriz, que precisam ser levantados e reimplementados, mas que podem ser compostos de qualquer coisa de entradas, menus suspensos ou 52 páginas de perguntas e dados.

Esse é o segundo exemplo: Coma um objeto, obtenha entradas de vários tipos, todas usando const como a variável base da biblioteca, sendo construídas.

http://codepen.io/jfrazz/pen/rWprVR/

Não exatamente o que você pediu, mas uma demonstração clara dessa constante pode ser bastante dinâmica.


[Thread longo, não tenho certeza se já está listado como uma opção ...].
Uma alternativa simples seria definir o const fora da classe. Isso só será acessível pelo próprio módulo, a menos que seja acompanhado por um getter.
Desta forma, o prototype não está cheio e você obtém o 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);
    }
}

Babel suporta variáveis ​​de classe no ESNext, verifique este example :

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'




ecmascript-6