#javascript #typescript
Вопрос:
У меня есть около 50 различных классов, которые расширяют базовый класс. Некоторые из них инициализируют внутренне некоторые другие. Все экземпляры обладают уникальным id
свойством
Цель состоит в том, чтобы знать, какой экземпляр инициировал какой (с некоторой ссылкой на Caller.id) и код для этого должен быть в контексте базового класса.
Все инициализации следуют этому формату, они вызывают «метод» сразу после инициализации: new ChildClass().method()
новый класс детей().метод().привязка(это), к сожалению, не лучший вариант в моем случае
Код выглядит так
class ChildClass extends BaseClass {
id = 'ChildId';
}
class ParentClass extends BaseClass{
id = 'ParentId';
parentMethod() {
new ChildClass().method()
}
}
abstract class BaseClass {
protected id;
private callerId;
constructor() {
// either here we set callerId from params
}
method() {
// or here we set callerId from params
}
}
Я хочу избежать следующего в моем классе родителей.
const callerId = this.id;
new ChildClass(callerId).method()
или
const callerId = this.id;
new ChildClass().method(callerId)
Но мне все еще нужен мой базовый класс, чтобы захватить идентификатор вызывающего абонента
Не уверен, что это вообще возможно 🙂
Комментарии:
1. Нет его javascript/машинописный текст 🙂
2. Вы говорите из парамов, но вы не проходите никаких парамов. Не совсем уверен, в чем заключается ваш вопрос здесь
3. В реальном коде я передаю кучу параметров, как при инициализации, так и при самом вызове метода. Вопрос в том, как НЕ передать идентификатор вызывающего абонента = this.id, но базовый класс должен каким-то образом захватить его автоматически. Родитель и ребенок здесь просто условности. У меня есть около 50 различных классов, которые расширяют базовый класс. Некоторые из них инициализируют внутренне некоторые другие. Все 50 содержат this.id Я просто хочу знать, какой экземпляр инициализировал какой экземпляр
4. Этот вопрос очень неясен.
5. Извините за это, не уверен, как сделать это более конкретным.
Ответ №1:
Если я вас правильно понял, вам нужно сохранить ссылку/ссылку на класс, который создал этот класс. Могут быть разные решения. Самое простое, что я предвижу, — это заменить new
в контексте вашего класса:
type Constructor<T> = { new (...args): T, prototype: T };
abstract class BaseClass {
protected id;
private callerId;
_new<T extends BaseClass>(clazz: Constructor<T>, ...args): T {
const i = new clazz(...args);
i.callerId = this.id;
return i;
}
method() {
}
}
class ChildClass extends BaseClass {
id = 'ChildId';
}
class ParentClass extends BaseClass{
id = 'ParentId';
parentMethod() {
this._new(ChildClass).method();
}
}
Я не полностью осведомлен обо всех вуду, которые можно сделать с помощью последних версий JS/TS. Насколько мне известно, вы ничего не можете сделать, если хотите сохранить свой код в форме:
new ChildClass().method();
Почему? Что ж, швейцарский нож для добавления магии в ваш JS-код есть Monkey patching
. Но по замыслу при использовании new
создается новый объект и передается функции конструктора в качестве текущего this
контекста. Такое поведение заставляет нас терять исходную ссылку, но ее нельзя изменить, следовательно, нет решения.
Я лгал
В JS вы можете подчинять правила своей воле (вот почему многие разработчики ненавидят это. Не я, конечно). Одно очень необычное решение, которое приходит мне в голову, вдохновлено Vue.js ядро привязки ценности, и оно будет работать следующим образом:
let __BASE_CLASS_ACT_ID = undefined; // (1)
abstract class BaseClass {
protected id;
private callerId;
constructor() {
// Catch my caller according to the current context
this.callerId = __BASE_CLASS_ACT_ID;
// Patch all my methods
for (const name of Object.getOwnPropertyNames(this)) {
if (typeof this[name] === 'function') {
const _old = this[name]; // (3)
this[name] = (...args) => { // (2)
__BASE_CLASS_ACT_ID = this.id;
_old.apply(this, args);
__BASE_CLASS_ACT_ID = undefined;
}
}
}
}
}
Здесь мы используем тот факт, что JS-это язык с одним потоком, поэтому любая функция будет вызываться одновременно. Мы сохраняем глобальную переменную (1)
, которая обновляется в функции исправления (2)
, которая устанавливает ее перед вызовом исходной функции (3)
и сбрасывает ее после. Это должно позволить вам заставить это работать:
parentMethod() {
new ChildClass().method();
}
Обратите внимание, что это не будет работать в конструкторе:
constructor() {
super();
new ChildClass().method(); // not working
}
Также он не будет работать в асинхронной функции:
parentMethod() {
doSome().then(x => {
new ChildClass().method(); // not working
})
}
Этот пример можно улучшить и усилить, но он всегда останется хак, где использование простого _new
решает ваши проблемы сразу.
Комментарии:
1. Спасибо за действительно полезные идеи, я узнал здесь несколько вещей. Пока
_new
что подход выглядит лучше. Этоmethod
действительно асинхронно, но это не единственная причина, по которой глобальное свойство не будет работать в моем случае использования, но в других случаях оно является надежным. Различные родительские классы также инициализируются параллельно, и, что еще хужеnew ChildClass
, код обычно используется в других асинхронных функциях. Глобалы-странная тема, но использование однопоточного выполнения и природы компилятора, на мой взгляд, не является взломом. 😀