Подтвердить что ты не робот

Альтернативные переменные класса ES6

В настоящее время в 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;
    }
}

К сожалению, вышеупомянутое не будет работать, поскольку классы могут содержать только методы.

Я понимаю, что я могу this.myVar = true в constructor... но я не хочу "мусор" моего конструктора, особенно когда у меня есть 20-30 + params для более крупного класса.

Я думал о многих способах решения этой проблемы, но пока не нашел хороших. (Например: создайте обработчик ClassConfig и передайте объект parameter, который объявляется отдельно от класса. Затем обработчик будет прикреплен к классу. Я думал о WeakMaps также как-то интегрироваться.)

Какими идеями вы должны справиться с этой ситуацией?

4b9b3361

Ответ 1

2018 обновление:

В настоящее время есть предложение этапа 3 - я с нетерпением жду, чтобы этот ответ устарел через несколько месяцев.

Тем временем любой, кто использует TypeScript или babel, может использовать синтаксис:

varName = value

Внутри объявления класса/тела выражения и он определит переменную. Надеюсь, через несколько месяцев/недель я смогу опубликовать обновление.

Обновление: Chrome 74 теперь поставляется с этим синтаксисом работает.


Примечания в вики ES для предложения в ES6 (максимально минимальные классы)

Не существует (намеренно) прямого декларативного способа определения свойств класса данных прототипа (кроме методов) или свойства экземпляра

Свойства класса и свойства данных прототипа должны быть созданы вне объявления.

Свойства, указанные в определении класса, назначаются тем же атрибутам, как если бы они появились в литерале объекта.

Это означает, что то, о чем вы просите, было рассмотрено и явно отклонено.

но почему?

Хороший вопрос. Хорошие люди из TC39 хотят, чтобы объявления классов объявляли и определяли возможности класса. Не его участники. Объявление класса ES6 определяет свой контракт для своего пользователя.

Помните, определение класса определяет методы прототипа - определение переменных в прототипе обычно не то, что вы делаете. Вы можете, конечно, использовать:

constructor(){
    this.foo = bar
}

В конструкторе, как вы предложили. Также см. Резюме консенсуса.

ES7 и выше

В настоящее время разрабатывается новое предложение для ES7, которое позволяет создавать более краткие переменные экземпляра с помощью объявлений и выражений классов - https://esdiscuss.org/topic/es7-property-initializers.

Ответ 2

Просто добавьте в Benjamin ответ - переменные класса возможны, но вы не использовали бы 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 действительно нет классов. Даже с ES6 мы рассматриваем язык на основе объекта или прототипа, а не язык на основе классов. В любом function X () {}, X.prototype.constructor указывает на X. Когда в X используется new оператор, создается новый объект, наследующий X.prototype. Любые свойства undefined в этом новом объекте (включая constructor) просматриваются оттуда. Мы можем думать об этом как о создании объектов и свойств класса.

Ответ 3

В вашем примере:

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'];}, то вывод предупреждения - строка, 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

Ответ 4

Babel поддерживает переменные класса в ESNext, проверьте этот пример:

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'

Ответ 5

Поскольку ваша проблема в основном стилистическая (не желая заполнять конструктор кучей деклараций), ее также можно решить стилистически.

Как я его просматриваю, многие языки на основе классов имеют конструктор как функцию, названную именем самого класса. Стилистически мы могли бы использовать это, чтобы сделать класс 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();
    }
}

Оба будут вызваны по мере создания нового экземпляра.

Сорта похожа на наличие двух конструкторов, в которых вы выделяете объявления и другие действия конструктора, которые вы хотите предпринять, и стилистически затрудняет понимание того, что происходит.

Я нахожу его приятным для использования при работе с большим количеством объявлений и/или множеством действий, которые необходимо выполнить при создании экземпляра, и желая, чтобы две идеи отличались друг от друга.


ПРИМЕЧАНИЕ. Я очень целенаправленно не использую типичные идиоматические идеи "инициализации" (например, метод init() или initialize()), поскольку они часто используются по-разному. Существует какая-то предполагаемая разница между идеей построения и инициализации. Работая с конструкторами, люди знают, что они вызываются автоматически как часть экземпляра. Увидев метод init, который многие люди предпочтут без второго взгляда, что им нужно что-то делать в форме var mc = MyClass(); mc.init();, так как вы обычно инициализируете. Я не пытаюсь добавить процесс инициализации для пользователя класса, я пытаюсь добавить в процесс построения самого класса.

В то время как некоторые люди могут сделать двойной прием на мгновение, это на самом деле бит точки: он сообщает им, что намерение является частью конструкции, даже если это заставляет их делать немного двойного действия и идти "это не то, как работают конструкторы ES6", а второй взгляд на фактический конструктор, чтобы он "о, они называют это внизу, я вижу", что намного лучше, чем НЕ сообщать о намерении (или неправильно обменивать его) и, вероятно, получать многие люди используют его неправильно, пытаясь инициализировать его извне и мусор. Это очень интенсивно для шаблона, который я предлагаю.


Для тех, кто не хочет следовать этому шаблону, может работать и полная противоположность. Сначала ферма объявляет другую функцию. Возможно, назовите его "свойства" или "publicProperties" или что-то еще. Затем поместите остальную часть материала в обычный конструктор.

"use strict";

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

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

Обратите внимание, что этот второй метод может выглядеть более чистым, но он также имеет неотъемлемую проблему, когда properties получает переопределение, поскольку один класс с использованием этого метода расширяет другой. Чтобы избежать этого, вы должны указать более уникальные имена properties. Мой первый метод не имеет этой проблемы, потому что его поддельная половина конструктора уникально названа после класса.

Ответ 6

Как насчет старинной школы?

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

В конструкторе вы указываете только те вары, которые нужно вычислить. Мне нравится наследование прототипа для этой функции - это может помочь сэкономить много памяти (в случае, если много никогда не назначенных варов).

Ответ 7

Как сказал Бенджамин в своем ответе, TC39 явно решил не включать эту функцию, по крайней мере, для ES2015. Однако, похоже, консенсус заключается в том, что они добавят его в ES2016.

Синтаксис еще не решен, но есть предварительное предложение для ES2016, которое позволит вам объявлять статические свойства в классе.

Благодаря волшебству babel вы можете использовать это сегодня. Включите преобразование свойств класса в соответствии с этими инструкциями, и вам хорошо идти. Вот пример синтаксиса:

class foo {
  static myProp = 'bar'
  someFunction() {
    console.log(this.myProp)
  }
}

Это предложение находится в очень раннем состоянии, поэтому будьте готовы изменить ваш синтаксис с течением времени.

Ответ 8

Длинная тема, не уверен, что она уже указана в качестве опции...
Простая альтернатива только для констант - определение константы вне класса. Это будет доступно только из самого модуля, если не сопровождается геттером.
Таким образом, 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);
    }
}

Ответ 9

Вы можете имитировать поведение классов 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

Ответ 10

ES7 члена класса ES7:

ES7 есть решение для "фальсификации" вашей функции конструктора. Вот пример:

class Car {
  
  wheels = 4;
  weight = 100;

}

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

Ответ 11

Как я решил это, что является еще одним вариантом (если у вас есть jQuery), нужно было определить поля в объекте старой школы, а затем расширить класс с помощью этого объекта. Я также не хотел, чтобы перетащить конструктор с помощью назначений, оказалось, что это опрятное решение.

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

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

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

- Обновление после комментария Берги.

Нет версии JQuery:

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

    }
}

У вас все еще есть конструктор "fat", но, по крайней мере, его все в одном классе и назначается одним ударом.

РЕДАКТИРОВАТЬ № 2: Теперь я прошел полный круг и теперь присваиваю значения в конструкторе, например.

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

Почему? Простой на самом деле, используя выше, плюс некоторые комментарии JSdoc, PHPStorm смог выполнить завершение кода в свойствах. Присвоение всех варов одним ударом было приятным, но неспособность кодировать свойства, imo, не стоит (почти наверняка, незначительной) производительности.

Ответ 12

Ну, вы можете объявить переменные внутри конструктора.

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

var foo = new Foo()

foo.method()

Ответ 13

Это немного хакерская комбинация статичности и работа для меня

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

Ответ 14

Можете ли вы избежать всей проблемы, используя сильные литералы и небольшую библиотеку логических схем шаблонов, содержащихся в большом закрытии?

игнорируя закрытие на данный момент

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

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

ЭТО - самый простой пример, который я могу предложить из репозитория. Первые 400 строк представляют собой библиотеку данных + некоторые основные служебные функции. Плюс несколько полезных констант.

После плиты котла, которую мы превращаем в данные, загруженные пользователями приложений, у нас есть массивные шаблоны, которые нужно снимать и перераспределять, но которые могут быть составлены из любых входов, выпадающих списков, или 52 страницы вопросов и данных.

Вот этот второй пример: Ешьте объект, получайте входы разных типов, все используя const в качестве базовой переменной библиотеки, создавая.

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

Не совсем то, что вы просили, но ясное представление о том, что константа может быть довольно динамичной.