英文:
Overwrite call in javascript object
问题
以下是您提供的代码的翻译部分:
class Z {
constructor() {}
a() {
console.log('a');
}
get b() {
console.log('b');
}
c() {
console.log('c');
}
}
class B {
constructor(z) {
this.z = z;
}
}
let z = new Z();
let b = new B(z);
b.a(); // 错误
b.b; // 错误
B
类旨在作为方法混合和变量捆绑,但出于某种原因,我无法将 Z
扩展到 B
。当我调用一个方法或检索 z
上的属性时,我能够检查是否可以通过 this.z
访问它,如果可以,直接返回它吗?
对于如何编写这个结构的任何想法将不胜感激。
我不能直接使用 extend
关键字的原因是,Z
由一个库提供,并且实例是在可调用或类的静态方法中构造的。我对“函数类构造函数”之类的东西一无所知,例如 _super
或 __proto__
。
我之所以考虑这一点,是因为你可以在 Python 中定义 __call__
或 __getitem__
来实现这一目的。我不确定这是否可能,如果你能帮助我,我将非常高兴。类似以下功能:
class Z {
a() {
try {
return this.z.a();
} catch (error) {}
}
}
但会适用于任何检索尝试。
感谢所有评论中的建议。
请注意以下不是最小工作示例,而是真实场景。
/*库源代码*/
function fromTextArea(textarea, options) {
options = options ? copyObj(options) : {};
options.value = textarea.value;
if (!options.tabindex && textarea.tabIndex) { options.tabindex = textarea.tabIndex; }
if (!options.placeholder && textarea.placeholder) { options.placeholder = textarea.placeholder; }
// 如果该文本区域聚焦或具有autofocus且没有其他元素聚焦,则将autofocus设置为true。
if (options.autofocus == null) {
var hasFocus = activeElt();
options.autofocus = hasFocus == textarea ||
textarea.getAttribute("autofocus") != null && hasFocus == document.body;
}
function save() { textarea.value = cm.getValue(); }
var realSubmit;
if (textarea.form) {
on(textarea.form, "submit", save);
// 使submit方法执行正确的可耻的hack。
if (!options.leaveSubmitMethodAlone) {
var form = textarea.form;
realSubmit = form.submit;
try {
var wrappedSubmit = form.submit = function () {
save();
form.submit = realSubmit;
form.submit();
form.submit = wrappedSubmit;
};
} catch (e) { }
}
}
options.finishInit = function (cm) {
cm.save = save;
cm.getTextArea = function () { return textarea; };
cm.toTextArea = function () {
cm.toTextArea = isNaN; // 防止运行两次
save();
textarea.parentNode.removeChild(cm.getWrapperElement());
textarea.style.display = "";
if (textarea.form) {
off(textarea.form, "submit", save);
if (!options.leaveSubmitMethodAlone && typeof textarea.form.submit == "function") { textarea.form.submit = realSubmit; }
}
};
};
textarea.style.display = "none";
var cm = CodeMirror(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); },
options);
return cm
}
// 我正在处理的应用程序的一部分
class CodeEditor {
constructor(
container,
generic,
types,
modes,
options,
) {
this.generic = generic;
this.types = types;
this.modes = modes;
/*
.code-container
.cm-header-bar
.cm-code-compile-wrapper
button.dropdown-button
textarea
*/
this.container = container;
this.textarea = container.querySelector('textarea');
this.header = this.container.querySelector('.cm-header-bar');
this.compileBtnWrapper = this.header.querySelector('.cm-code-compile-wrapper');
this.compileBtn = document.createElement('div');
this.compileBtn.classList.add('cm-code-compile');
this.compileBtn.innerText = 'compile';
this.options = options;
this.mode = this.textarea.getAttribute('id');
this.options.mode.name = this.mode;
this.editor = CodeMirror.fromTextArea(this.textarea, this.options);
// 编辑器设置
this.editor.on("gutterClick", function (cm, n) {
let info = cm.lineInfo(n);
cm.setGutterMarker(n, "breakpoints", info.gutterMarkers ? null : makeMarker());
});
// 可编译
if (this.mode !== this.generic) this.compilable();
this.dropdown = dropdown(this.header.querySelector('.dropdown-button'), '预先处理', generic);
Object.keys(this.modes).forEach(mode => {
this.dropdown.addOption(mode, () => {
htmlEditor.setOption('mode', { name: this.name, globalVars: true });
this.mode = mode;
this.editor.refresh();
play();
if (mode !== this.generic) this.compilable();
}, mode === this.mode);
});
}
get name() {
return this.types[this.mode];
}
compilable() {
this.compileBtnWrapper.appendChild(this.compileBtn);
let temp = {};
const oxidize = () => {
this.compileBtn.onclick = () => {
temp.code = this.getValue();
temp.mode = this.mode;
// 编译
this.dropdown.onOption(this.generic);
this.setValue(this.modes[this.mode]());
this.options.mode.name = this.types[this.generic];
this.setOption('mode', this.options.mode);
this.compileBtn.classList.add('active');
this.compileBtn.innerText = 'restore';
// 撤销
reduce();
}
}
const reduce = () => {
this.compileBtn.onclick = () => {
this.dropdown.onOption(temp.mode);
this.setValue(temp.code);
this.options.mode.name = temp.mode;
this.setOption('mode', this.types[temp.mode]);
this.compileBtn.classList.remove('active');
this.compileBtn.innerText = 'compile';
// 撤销
oxidize();
}
}
oxidize();
}
/*
我正在优化一个大型项目,实例以前是一个
<details>
<summary>英文:</summary>
I am a newbie in JavaScript, trying to extend class variable prototype without inheritance
```js
class Z{
constructor(){}
a(){console.log('a')}
get b(){console.log('b')}
c(){console.log('c')}
}
class B{
constructor(z){
this.z = z;
}
}
let z = new Z();
let b = new B(z);
b.a(); // error
b.b; // error
the B
class is intended to be just a method mixin and variable bundle, however for some reason, I cannot extend Z
to B
. When I calling a method, or retrieve a prop
on z
, can I inspect whether it can be accessed through this.z
, if so, return it directly?
Any ideas of how to write this structure will be more than appreciated.
The reason I cannot use extend
keyword directly, is that Z
is provided by a lib, and the instance is constructed in a callable, or static on class. I am not familiar with function class constructor
at all, things such as _super
or __proto__
.
And why I thought about this is that, you can define a dunder __call__
or __getitem__
in python to serve this purpose. I am not sure if it is possible at all, I will be so glad if you can give me a hand. Something functional like:
class Z{
a()
try{
return this.z.a()
}catch(){/}
}
}
But would apply for any retrieving attempt.
Thanks all comments for suggestion.
Note following are not minimum working example but real life occasion.
/* lib source code */
function fromTextArea(textarea, options) {
options = options ? copyObj(options) : {};
options.value = textarea.value;
if (!options.tabindex && textarea.tabIndex) { options.tabindex = textarea.tabIndex; }
if (!options.placeholder && textarea.placeholder) { options.placeholder = textarea.placeholder; }
// Set autofocus to true if this textarea is focused, or if it has
// autofocus and no other element is focused.
if (options.autofocus == null) {
var hasFocus = activeElt();
options.autofocus = hasFocus == textarea ||
textarea.getAttribute("autofocus") != null && hasFocus == document.body;
}
function save() { textarea.value = cm.getValue(); }
var realSubmit;
if (textarea.form) {
on(textarea.form, "submit", save);
// Deplorable hack to make the submit method do the right thing.
if (!options.leaveSubmitMethodAlone) {
var form = textarea.form;
realSubmit = form.submit;
try {
var wrappedSubmit = form.submit = function () {
save();
form.submit = realSubmit;
form.submit();
form.submit = wrappedSubmit;
};
} catch (e) { }
}
}
options.finishInit = function (cm) {
cm.save = save;
cm.getTextArea = function () { return textarea; };
cm.toTextArea = function () {
cm.toTextArea = isNaN; // Prevent this from being ran twice
save();
textarea.parentNode.removeChild(cm.getWrapperElement());
textarea.style.display = "";
if (textarea.form) {
off(textarea.form, "submit", save);
if (!options.leaveSubmitMethodAlone && typeof textarea.form.submit == "function") { textarea.form.submit = realSubmit; }
}
};
};
textarea.style.display = "none";
var cm = CodeMirror(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); },
options);
return cm
}
// a part of the application I am working on
class CodeEditor{
constructor(
container,
generic,
types,
modes,
options,
){
this.generic = generic;
this.types = types;
this.modes = modes;
/*
.code-container
.cm-header-bar
.cm-code-compile-wrapper
button.dropdown-button
textarea
*/
this.container = container;
this.textarea = container.querySelector('textarea');
this.header = this.container.querySelector('.cm-header-bar');
this.compileBtnWrapper = this.header.querySelector('.cm-code-compile-wrapper');
this.compileBtn = document.createElement('div');
this.compileBtn.classList.add('cm-code-compile');
this.compileBtn.innerText = 'compile';
this.options = options;
this.mode = this.textarea.getAttribute('id');
this.options.mode.name = this.mode;
this.editor = CodeMirror.fromTextArea(this.textarea, this.options);
// editor setup
this.editor.on("gutterClick", function(cm, n) {
let info = cm.lineInfo(n);
cm.setGutterMarker(n, "breakpoints", info.gutterMarkers ? null : makeMarker());
});
// compilable
if(this.mode !== this.generic)this.compilable();
this.dropdown = dropdown(this.header.querySelector('.dropdown-button'), '预先处理', generic);
Object.keys(this.modes).forEach(mode=>{
this.dropdown.addOption(mode, ()=>{
htmlEditor.setOption('mode', { name: this.name, globalVars: true });
this.mode = mode;
this.editor.refresh();
play();
if(mode !== this.generic)this.compilable();
}, mode === this.mode);
});
}
get name(){
return this.types[this.mode];
}
compilable(){
this.compileBtnWrapper.appendChild(this.compileBtn);
let temp = {};
const oxidize = () => {
this.compileBtn.onclick = () => {
temp.code = this.getValue();
temp.mode = this.mode ;
// compile
this.dropdown.onOption(this.generic);
this.setValue(this.modes[this.mode]());
this.options.mode.name = this.types[this.generic];
this.setOption('mode', this.options.mode);
this.compileBtn.classList.add('active');
this.compileBtn.innerText = 'restore';
// undo
reduce();
}
}
const reduce = () => {
this.compileBtn.onclick = () => {
this.dropdown.onOption(temp.mode);
this.setValue(temp.code);
this.options.mode.name = temp.mode;
this.setOption('mode', this.types[temp.mode]);
this.compileBtn.classList.remove('active');
this.compileBtn.innerText = 'compile';
// undo
oxidize();
}
}
oxidize();
}
/*
I am optimizing a big proj, the instance used to be a
CodeMirror. However, it produces a lot of redundant
self-occurring parts, and I do not want to pass parameters
all day, therefore I wrapped it with a CodeEditor instance
However, other code in this project would call the CodeMirror
prototype method, since I cannot find a way to extends
CodeMirror methods from this.editor to this, I need to
redefine them all (following are only a tiny part)
*/
setValue(v){return this.editor.setValue(v)}
getValue(){return this.editor.getValue()}
setOption(...args){return this.editor.setOption(...args)};
focus(){return this.editor.focus()}
// more functionality
compiled(){return this.modes[this.mode]() || ''};
raw(){return this.getValue().trimEnd()}
}
CodeMirror 5.63.3: https://codemirror.net/5/doc/releases.html
Sorry that I did not find a CDN that provides non-minimised code.
答案1
得分: 1
以下是翻译好的内容,不包括代码部分:
You could inject the Z
prototype in the prototype chain like this:
你可以像这样将 Z
的原型注入原型链中:
Object.setPrototypeOf(B.prototype, Z.prototype);
This establishes this chain:
这将建立这个链:
b → B.prototype → Z.prototype → Object.prototype
If you need b
to also have access to whatever instance properties have been set on z
(so not on its proto object), then add one more step in the chain like this:
如果你需要 b
也能访问在 z
上设置的实例属性(而不是在其原型对象上),那么可以像这样在链中添加一步:
Object.setPrototypeOf(B.prototype, z);
This establishes this chain:
这将建立这个链:
b → B.prototype → z → Z.prototype → Object.prototype
In case b
must be created as an instance of Z.prototype
, and not of B.prototype
, then you should actually not define the B
class at all, but define a decorator function:
如果必须将 b
创建为 Z.prototype
的实例,而不是 B.prototype
的实例,那么实际上你不应该定义 B
类,而应该定义一个装饰器函数:
function decorateNewZ() {
return Object.assign(new Z(), {
// Any extra methods
x() { console.log('x') }
});
}
英文:
You could inject the Z
prototype in the prototype chain like this:
Object.setPrototypeOf(B.prototype, Z.prototype);
This establishes this chain:
b → B.prototype → Z.prototype → Object.prototype
<!-- begin snippet: js hide: true console: true babel: false -->
<!-- language: lang-js -->
// Library code
class Z{
constructor() {}
a() { console.log('a') }
get b() { console.log('b') }
c() { console.log('c') }
}
const z = new Z();
// End library code
class B{
constructor() {}
x() { console.log('x') }
}
Object.setPrototypeOf(B.prototype, Z.prototype);
let b = new B();
b.a();
b.b;
b.x();
z.test = 1;
console.log(b.test); // Undefined (see next snippet)
<!-- end snippet -->
If you need b
to also have access to whatever instance properties have been set on z
(so not on its proto object), then add one more step in the chain like this:
Object.setPrototypeOf(B.prototype, z);
This establishes this chain:
b → B.prototype → z → Z.prototype → Object.prototype
<!-- begin snippet: js hide: true console: true babel: false -->
<!-- language: lang-js -->
// Library code
class Z{
constructor() {}
a() { console.log('a') }
get b() { console.log('b') }
c() { console.log('c') }
}
const z = new Z();
// End library code
class B{
constructor() {}
x() { console.log('x') }
}
Object.setPrototypeOf(B.prototype, z);
let b = new B();
b.a();
b.b;
b.x();
z.test = 1;
console.log(b.test); // 1
<!-- end snippet -->
In case b
must be created as an instance of Z.prototype
, and not of B.prototype
, then you should actually not define the B
class at all, but define a decorator function:
function decorateNewZ() {
return Object.assign(new Z(), {
// Any extra methods
x() { console.log('x') }
});
}
<!-- begin snippet: js hide: true console: true babel: false -->
<!-- language: lang-js -->
// Library code
class Z{
constructor() {}
a() { console.log('a') }
get b() { console.log('b') }
c() { console.log('c') }
}
const z = new Z();
// End library code
function decorateZ() {
return Object.assign(new Z(), {
// Any extra methods
x() { console.log('x') }
});
}
let b = decorateZ();
b.a();
b.b;
b.x();
<!-- end snippet -->
答案2
得分: 1
An entirely ES6-class based approach provides everything (all of the necessary prototype (re)wiring) for free.
The following implementation returns a class which extends the constructor of any provided type-instance (either of class' or of pure constructor functions or of built-ins). It also supports the naming of the returned class constructor.
function createExtendedClassFromInstanceType(
className, instance
) {
const { constructor: SuperClass } = instance;
if ('function' === typeof SuperClass) {
return ({
[className]: class extends SuperClass {
constructor(...args) {
super(...args);
// own implementation
}
}
})[className];
}
}
class NonAccessibleType {
constructor() {
}
// all prototypal
a() {
console.log('`a()` logs from custom prototype');
}
get b() {
console.log('`b` getter logs from custom prototype');
}
c () {
console.log('`c()` logs from custom prototype');
}
}
const customInstance = new NonAccessibleType;
const SubType = createExtendedClassFromInstanceType(
'SubType', customInstance
);
let subType = new SubType;
console.log('invoking `subType.a()` ...');
subType.a();
console.log('accessing `subType.b` ...');
subType.b;
console.log(
'\ncustomInstance.constructor.name ...',
customInstance.constructor.name
);
console.log(
'subType.constructor.name ...',
subType.constructor.name
);
console.log(
'\n(customInstance instanceof NonAccessibleType) ?..',
(customInstance instanceof NonAccessibleType)
);
console.log(
'(customInstance instanceof SubType) ?..',
(customInstance instanceof SubType)
);
console.log(
'(subType instanceof NonAccessibleType) ?..',
(subType instanceof NonAccessibleType)
);
console.log(
'(subType instanceof SubType) ?..',
(subType instanceof SubType)
);
Note, regarding the OP's mentioning of...
"The B class is intended to be just a method mixin and variable bundle..."
... the OP might have a look into the answer of "How to create an extended ES6-class constructor from a provided base class and from additionally provided and to be mixed-in behavior?" where the same base technique of a class creating factory got used for covering also the mixin aspect instead of just the one of inheritance.
英文:
An entirely ES6-class based approach provides everything (all of the necessary prototype (re)wiring) for free.
The following implementation returns a class which extends the constructor of any provided type-instance (either of class' or of pure constructor functions or of built-ins). It also supports the naming of the returned class constructor.
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
function createExtendedClassFromInstanceType(
className, instance
) {
const { constructor: SuperClass } = instance;
if ('function' === typeof SuperClass) {
return ({
[className]: class extends SuperClass {
constructor(...args) {
super(...args);
// own implementation
}
}
})[className];
}
}
class NonAccessibleType {
constructor() {
}
// all prototypal
a() {
console.log('`a()` logs from custom prototype');
}
get b() {
console.log('`b` getter logs from custom prototype');
}
c () {
console.log('`c()` logs from custom prototype');
}
}
const customInstance = new NonAccessibleType;
const SubType = createExtendedClassFromInstanceType(
'SubType', customInstance
);
let subType = new SubType;
console.log('invoking `subType.a()` ...');
subType.a();
console.log('accessing `subType.b` ...');
subType.b;
console.log(
'\ncustomInstance.constructor.name ...',
customInstance.constructor.name
);
console.log(
'subType.constructor.name ...',
subType.constructor.name
);
console.log(
'\n(customInstance instanceof NonAccessibleType) ?..',
(customInstance instanceof NonAccessibleType)
);
console.log(
'(customInstance instanceof SubType) ?..',
(customInstance instanceof SubType)
);
console.log(
'(subType instanceof NonAccessibleType) ?..',
(subType instanceof NonAccessibleType)
);
console.log(
'(subType instanceof SubType) ?..',
(subType instanceof SubType)
);
<!-- language: lang-css -->
.as-console-wrapper { min-height: 100%!important; top: 0; }
<!-- end snippet -->
Note, regarding the OP's mentioning of ...
> "... the B class is intended to be just a method mixin and variable bundle ..."
... the OP might have a look into the answer of "How to create an extended ES6-class constructor from a provided base class and from additionally provided and to be mixed-in behavior?" where the same base technique of a class creating factory got used for covering also the mixin aspect instead of just the one of inheritance.
答案3
得分: 0
抱歉,JavaScript不像Python一样提供getattr
,因此您最好的选择是手动创建代理方法,例如:
class B {
constructor(z) {
let proto = Object.getPrototypeOf(z)
for (let p of Object.getOwnPropertyNames(proto)) {
let val = proto[p]
if (typeof val === 'function')
this[p] = val.bind(z)
}
}
}
请注意,val.bind(z)
将在传递的 z
对象(“外观”)的上下文中调用 Z
方法。您也可以使用 val.bind(this)
,这将使用 B
对象作为上下文(“混入”)。
英文:
Unfortunately, javascript doesn't provide getattr
like in python, so your best bet is to create proxy methods manually, for example:
class B {
constructor(z) {
let proto = Object.getPrototypeOf(z)
for (let p of Object.getOwnPropertyNames(proto)) {
let val = proto
if (typeof val === 'function')
this
= val.bind(z)
}
}
}
Note that val.bind(z)
would invoke Z
methods in the context of the passed z
object (="facade"). You can also do val.bind(this)
, which would use the B
object as a context (="mixin").
答案4
得分: 0
以下是您要翻译的内容:
You can use a Proxy although Proxies ain't the most performant thing in JS.
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
class Bar {
value = 0;
foo() {
return "Bar.foo()";
}
bar(val) {
this.value = val;
}
}
class Foo {
constructor() {
//this.editor = new Bar();
this.$editor = new Bar();
}
}
Object.setPrototypeOf(Foo.prototype, new Proxy(
Object.getPrototypeOf(Foo.prototype),
{
get(t, p, r) {
//return Reflect.get(p !== "editor" && r.editor || t, p);
return Reflect.get(r.$editor, p);
},
})
)
var f = new Foo();
console.log(f.foo());
console.log(f.value);
f.bar(42);
console.log(f.value);
<!-- end snippet -->
英文:
You can use a Proxy although Proxies ain't the most performant thing in JS.
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
class Bar {
value = 0;
foo() {
return "Bar.foo()";
}
bar(val) {
this.value = val;
}
}
class Foo {
constructor() {
//this.editor = new Bar();
this.$editor = new Bar();
}
}
Object.setPrototypeOf(Foo.prototype, new Proxy(
Object.getPrototypeOf(Foo.prototype),
{
get(t, p, r) {
//return Reflect.get(p !== "editor" && r.editor || t, p);
return Reflect.get(r.$editor, p);
},
})
)
var f = new Foo();
console.log(f.foo());
console.log(f.value);
f.bar(42);
console.log(f.value);
<!-- end snippet -->
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论