Учитывая экземпляр v
класса Vector
, скажем v = new Vector(3, 5, 7)
, возможно ли как-то использовать синтаксис v(k)
для вызова определенного метода класса Vector
на v
с аргументом(ами) k
?
Чтобы обеспечить некоторый контекст, я хотел бы иметь возможность использовать v(k)
для вызова метода getElem(k)
для v
, который извлекает k
-й элемент вектора v
. Например, v(2)
вернет 7
. Другими словами, v(k)
будет действовать как псевдоним или сокращение для v.getElem(k)
.
Конечно, можно было бы написать (пользовательский) препроцессор для достижения вышеизложенного, мне просто интересно, есть ли встроенный способ реализовать это.
Этот вопрос был навеян синтаксисом библиотеки C++ Eigen
, которая позволяет получать/устанавливать матричные элементы аналогичным образом. Было бы здорово иметь что-то подобное в JavaScript.
Немного кода для сопровождения упомянутого выше класса —
class Vector {
constructor(...vecElems) {
this.vecElems = vecElems;
}
getElem(k) {
return this.vecElems[k];
}
dot(v) {
return this.vecElems.reduce((aV, cV, cI) => aV + cV * v.vecElems[cI], 0);
}
}
const v = new Vector(3, 5, 7);
const w = new Vector(4, 6, 8);
console.info(v.getElem(2), v.dot(w));
По сути, вам нужно v
быть функцией в первую очередь, со свойствами и методами по мере необходимости. Однако я не думаю, что вы можете сделать это с синтаксисом класса.
Что ж, эта часть возможна, если поместить return (i) => this.vecElems[i];
в конструктор, но тогда вы потеряете другие функции. Почему вам нужен именно этот синтаксис? Вы просто излишне усложняете.
@0stone0 Действительно.
@SebastianSimon Я привык к похожему синтаксису при работе с Eigen
, и мне стало любопытно, возможно ли это в JS. Если это так, было бы легко обобщить это на многомерную настройку, то есть на работу с классами матриц/тензоров.
Вы также можете заставить синтаксис v[i]
возвращать v.getElem(i)
, создав подкласс самого Array
:
class Vector extends Array {
constructor(...vecElems) {
super(...vecElems);
}
getElem(k) {
return this[k];
}
dot(v) {
return this.reduce((aV, cV, cI) => aV + cV * v[cI], 0);
}
}
const v = new Vector(3, 5, 7);
const w = new Vector(4, 6, 8);
console.info(v.getElem(0), v.getElem(1));
console.info(v[0], v[1]);
console.info(v.dot(w));
Интересная альтернатива, о которой я как-то не подумал. Спасибо!
Кажется, вам нужен генератор объектов (т.е. класс), который возвращает функции, которые также имеют ваши настраиваемые свойства.
Вот как вы можете создать такой «класс»:
function Vector(...vecElems) {
let members = {
vecElems,
getElem: function (k) {
return this.vecElems[k];
},
dot: function (v) {
return this.vecElems.reduce((aV, cV, cI) => aV + cV * v.vecElems[cI], 0);
}
};
return Object.assign(members.getElem.bind(members), members);
}
const v = new Vector(3, 5, 7);
const w = new Vector(4, 6, 8);
console.info(v.getElem(0), v.getElem(1));
console.info(v.dot(w));
console.info(v(0), v(1));
Это будет более производительно, чем синтаксис класса ES6 с подходом Proxy
, потому что доступ к членам через Proxy
страдает косвенностью.
Наконец, был указан довольно элегантный, безопасный для инъекций способ сделать это в синтаксисе класса ES6+ с прокси:
class Vector extends Function {
constructor(...vecElems) {
super();
this.vecElems = vecElems;
return new Proxy(this, {
apply(target, _, args) {
return target.getElem(...args);
}
});
}
getElem(k) {
return this.vecElems[k];
}
dot(v) {
return this.vecElems.reduce((aV, cV, cI) => aV + cV * v.vecElems[cI], 0);
}
}
const v = new Vector(3, 5, 7);
const w = new Vector(4, 6, 8);
console.info(v.getElem(2), v.dot(w));
console.info(v(2), v.dot(w));
Здесь используется Proxy
handler.apply для переноса вызываемого объекта, которым в данном случае является сам экземпляр класса, потому что он расширяет Function
. Он должен расширять Function
, чтобы синтаксис вызова x(...)
был действительным.
Большой! Спасибо за все усилия (включая перефразировку исходного вопроса), очень признателен.
Итак, вы хотите
v(k)
вернутьсяthis.vecElements[k]
?