覆盖javascript对象中的调用

2mbi3lxu  于 2023-03-11  发布在  Java
关注(0)|答案(4)|浏览(135)

我是一个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,提供非最小化代码。

xienkqul

xienkqul1#

你可以使用一个代理,虽然代理在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);
4c8rllxm

4c8rllxm2#

您可以将Z原型注入原型链,如下所示:

Object.setPrototypeOf(B.prototype, Z.prototype);

这就建立了这个链:

b → B.prototype → Z.prototype → Object.prototype
// 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)

如果你需要b能够访问z上设置的任何示例属性(而不是它的proto对象),那么在链中再添加一个步骤,如下所示:

Object.setPrototypeOf(B.prototype, z);

这就建立了这个链:

b → B.prototype → z → Z.prototype → Object.prototype
// 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

如果b必须创建为Z.prototype的示例,而不是B.prototype的示例,那么实际上根本不应该定义B类,而是定义一个装饰器函数:

function decorateNewZ() {
    return Object.assign(new Z(), {
        // Any extra methods
        x() { console.log('x') }
    });
}
// 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();
1bqhqjot

1bqhqjot3#

不幸的是,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方法(=“facade”),您也可以执行val.bind(this),它将使用B对象作为上下文(=“mixin”)。

6mzjoqzu

6mzjoqzu4#

完全基于ES6类的方法免费提供了几乎所有必要的原型(重新)布线。
下面的实现返回一个类,该类扩展了任何提供的类型示例(类'、纯构造函数或内置函数)的构造函数。它还支持命名返回的类构造函数。

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)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }

*,关于执行部分提到. *

  • ...“B类只是一个方法混合和变量绑定..."*

... OP可能会查看 “如何从提供的基类和额外提供的并要混合的行为创建扩展的ES6类构造函数?" 的答案,其中类创建工厂的相同基本技术也用于覆盖混合方面,而不仅仅是继承方面。

相关问题