我是一个JavaScript新手,尝试在没有继承的情况下扩展类变量原型
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
B
类本来只是一个方法mixin和变量bundle,但是由于某种原因,我不能把Z
扩展为B
,当我调用一个方法,或者在z
上检索一个prop
时,我能不能检查一下它是否可以通过this.z
访问,如果可以,直接返回它?
任何关于如何编写此结构的想法都将非常感谢。
我不能直接使用extend
关键字的原因是Z
是由一个库提供的,并且示例是在一个可调用的或静态的类中构造的。我对function class constructor
一点也不熟悉,比如_super
或__proto__
。
我想到这一点的原因是,你可以在python中定义一个dunder __call__
或__getitem__
来服务于这个目的。我不确定这是否可能,如果你能给予我一把,我会很高兴。一些功能如下:
class Z{
a()
try{
return this.z.a()
}catch(){/}
}
}
但会适用于任何检索尝试。
感谢所有的意见建议。注意以下不是最低限度的工作例子,但真实的生活中的场合。
/* 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()}
}
代码镜像5.63.3:https://codemirror.net/5/doc/releases.html
对不起,我没有找到一个CDN,提供非最小化代码。
4条答案
按热度按时间xienkqul1#
你可以使用一个代理,虽然代理在JS中不是最好的。
4c8rllxm2#
您可以将
Z
原型注入原型链,如下所示:这就建立了这个链:
如果你需要
b
也能够访问z
上设置的任何示例属性(而不是它的proto对象),那么在链中再添加一个步骤,如下所示:这就建立了这个链:
如果
b
必须创建为Z.prototype
的示例,而不是B.prototype
的示例,那么实际上根本不应该定义B
类,而是定义一个装饰器函数:1bqhqjot3#
不幸的是,javascript不像python那样提供
getattr
,所以你最好的选择是手工创建代理方法,例如:注意,
val.bind(z)
将在传递的z
对象的上下文中调用Z
方法(=“facade”),您也可以执行val.bind(this)
,它将使用B
对象作为上下文(=“mixin”)。6mzjoqzu4#
完全基于ES6类的方法免费提供了几乎所有必要的原型(重新)布线。
下面的实现返回一个类,该类扩展了任何提供的类型示例(类'、纯构造函数或内置函数)的构造函数。它还支持命名返回的类构造函数。
*注,关于执行部分提到. *
... OP可能会查看 “如何从提供的基类和额外提供的并要混合的行为创建扩展的ES6类构造函数?" 的答案,其中类创建工厂的相同基本技术也用于覆盖混合方面,而不仅仅是继承方面。