Как разбить статические методы в отдельный файл и правильно экспортировать через ES6?

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

Этот вопрос нашел хороший ответ для методов экземпляра, но хороший ответ для статических фабричных функций все еще ускользает. Рассмотрим этот класс ES6:

const fnCreate = require('./create');
const fnInstanceMethod = require('./instanceMethod');

class Door {
  constructor() {
    this.species = 'Oak';
    this.instanceMethod = fnInstanceMethod; // <-- works great
  }

  // Cannot connect static to required files...
  static create = fnCreate; // Syntax error unexpected '='

  // so static has to be written in class..
  static create() {
    return new Door(); // <-- the only line in fnCreate
  }
}
module.exports = Door; // but it exports and works fine...

Вы можете видеть, где было бы очень приятно просто потребовать статическую функцию, но я не нашел способа сделать это с ES6; это должно быть реализовано в файле класса. Итак, вопрос 1 из 2: "Есть ли способ потребовать в фабричной функции и подключить ее статически в классах ES6?"

Эта «неспособность» ES6 заставила меня переписать ES5 следующим образом:

const fnCreate = require('./create');
const fnInstanceMethod = require('./instanceMethod');

function Door() {
  this.species = 'Oak';
  this.instanceMethod = fnInstanceMethod; // <-- works great
}

Door.create = fnCreate; // <-- totally solves the file problem; works fine

module.exports = Door; // <-- NOPE! Node says "Door" is not a constructor"

Это позволяет избежать одной проблемы и перейти к другой. В «обычном» JS этот код работает нормально, а Door.create() возвращает новый объект Door. Но экспорт каким-то образом ломает реализацию, выдавая ошибку «Дверь не является конструктором». Вопрос 2 из 2: "Можно ли экспортировать приведенный выше код ES5, чтобы он работал правильно?"


person Geek Stocks    schedule 11.12.2018    source источник
comment
Я не уверен в вашей "Door" is not a constructor" ошибке jsfiddle.net/9d5xo7ac Если fnCreate находится в отдельном файле, не будет иметь область действия Door, поэтому я думаю, что ошибка будет Uncaught ReferenceError: Door is not defined при вызове create() (то есть fnCreate)   -  person CertainPerformance    schedule 11.12.2018
comment
Возможный дубликат, но, по крайней мере, связанный с Как динамически создать статический метод в JavaScript?   -  person Felix Kling    schedule 11.12.2018
comment
classes - это более или менее просто синтаксический сахар. Door — это функция, независимо от того, используете ли вы class Door {} или function Door() {}.   -  person Felix Kling    schedule 11.12.2018
comment
@FelixKling - очень полезная ссылка, спасибо   -  person Geek Stocks    schedule 11.12.2018


Ответы (1)


С классом ES6 вы можете назначать объект класса и иметь то, что по сути является статическим методом, точно так же, как в ES5:

class Door {
  constructor() {
    this.species = 'Oak';
    this.instanceMethod = fnInstanceMethod; // <-- works great
  }
}
Door.create = fnCreate;

Обратите внимание, что ваш

this.instanceMethod = fnInstanceMethod; // <-- works great

хотя это работает, это, вероятно, не лучшая идея — если вы хотите имитировать стандартное наследование от прототипа (например, когда разделение вещей по нескольким файлам не требуется), вы должны присвоить объекту прототипа вместо этого, как и в ES5:

class Door {
  constructor() {
    this.species = 'Oak';
  }
}
Door.create = fnCreate;
Door.prototype.instanceMethod = fnInstanceMethod;

Я не уверен насчет ошибки «Дверь не является конструктором», но с вашей текущей реализацией fnCreate (в отдельном файле) не будет иметь область Door, поэтому fnCreate не сможет ссылаться и создайте файл new Door. Одним из способов решения этой проблемы может быть экспорт функции, которая принимает Door в качестве входных данных и возвращает функцию, которая возвращает экземпляр:

// makeCreate.js
module.exports = theClass => () => new theClass();

// Door.js
const makeCreate = require('./makeCreate');
const fnInstanceMethod = require('./instanceMethod');
class Door {
  constructor() {
    this.species = 'Oak';
  }
}
Door.create = makeCreate(Door);
Door.prototype.instanceMethod = fnInstanceMethod;

module.exports = Door;

Или makeCreate в ES5:

function makeCreate(theClass) {
  return function() {
    return new theClass();
  }
}
module.exports = makeCreate;
person CertainPerformance    schedule 11.12.2018
comment
Я только что проверил это и да! оно работает. Не думал, что ES5 и ES6 можно смешивать таким образом. И module.exports тоже отлично работает. Не могли бы вы отредактировать свой код, включив в него module.exports = Door;, чтобы мы оставили после себя полный ответ? Спасибо! - person Geek Stocks; 11.12.2018
comment
ваша вложенная строка функции стрелки ( module.exports = theClass => () => new theClass(); ) - это какое-то серьезное темное искусство ES6, лол. Не могли бы вы немного распаковать это? Может быть, переписать ES5, чтобы помочь понять? - person Geek Stocks; 11.12.2018
comment
Смотрите редактирование, хотя оно добавляет много дополнительного синтаксического шума IMO - person CertainPerformance; 11.12.2018
comment
полностью согласен, люблю функции стрелок, но вложенная природа этого драгоценного камня не очевидна, ИМО, даже после некоторого изучения. Я ценю ваше время. - person Geek Stocks; 11.12.2018