如何在这个类中使用ES6类在运行时更改类'原型

时间:2018-05-03 18:26:50

标签: javascript ecmascript-6

我有以下“X”类,我想动态地向其prototype添加一些内容,就像这样(工作):

    class X{
        someUnrelatedFunction(){};	
    }
    
    //adding some stuff at runtime, in real situation, the ['a','b','c'] aren't "known" in advance like here.

    ['a','b','c'].forEach(elem => {
    	X.prototype[elem] = () =>{
      	    console.log('called with : ' + elem);
      };
    })

    //tests:

    x = new X();
    x.c();//works, outputs 'called with : c';

{p> class声明中。我想这样做,以使事情更具可读性,即我希望“原型”初始化属于类本身。

现在我正在'构造函数'中这样做,就像这里:

class X{
	constructor(){
  	//we don't want to do that again:
  	if(typeof X.prototype.inited === 'undefined'){
    	X.prototype.inited = true;
    	console.log('-constructor: initing prototype');
      ['a','b','c'].forEach(elem => {
        X.prototype[elem] = () =>{
          console.log('called with : ' + elem);
        };
      })      
  	}else{
    	console.log('-constructor: no need to re-init prototype');
    }	
  }
}


x1 = new X();
x1.a();

x2 = new X();
x2.b();

小提琴:https://jsfiddle.net/nypL29f3/4/

但这对我来说似乎很棘手,而且在课堂上我实际上引用它有点“外面”,即我正在使用“X”。如果我更改了类名,我还需要更改该部分代码。

所以问题是 - 如何以更可读和正确的方式做到这一点?

FWIW,“真实”的情况是我通过创建像这样的无用脚本来玩ES6:

https://jsfiddle.net/kpion/mqyrewnx/9/

我怀疑是关于第78行//这是我的问题 - 似乎我需要在类YaLog之外进行。

编辑:只是顺便说一句 - 如果你带着类似的问题来到这里 - 以下所有答案都会以某种方式回答这个问题,所有这些都值得一读。感谢大家! :)

3 个答案:

答案 0 :(得分:2)

  

我想要'原型'初始化属于类本身。

这是不可能的。类主体只能包含方法定义,而不能包含任意代码。

如果已知方法的数量且只有它们的名称是动态的,则可以使用计算属性名称,但在您的示例中似乎并非如此。

由于您正在寻找更易读的方法,因此只需将类和静态值的所有赋值或动态创建的方法放在单独的模块中。 (这可能是ES6模块,IIFE模块或用于视觉分离的简单块范围)。

  

现在我在constructor

中执行此操作

唐'吨。这是低效的,容易出错且非常难以理解。

答案 1 :(得分:2)

这种类原型修改永远不会从构造函数内部执行 - 主要是因为类构造函数负责管理当前的类实例,而不是所有类实例。构造函数应该在this类实例上声明新方法('log','info'等),这比在类prototype上声明方法效率低,并且对于进一步的可能是可取的或不可取的类继承。

这就是装饰者的目的。它们为扩展或修改类构造函数和原型提供了方便的语法。

装饰者可以修改现有的班级prototype

function withLogger(Class) {
  ['log','info','error','warn','group','groupEnd'].forEach(func => {
    Class.prototype[func] = function() {
      return this.callConsoleMethod(func, ...arguments);
    };
  });

  return Class;
}

或者返回一个以非破坏性的方式扩展现有类的新类,这允许在包装类中隐藏它们时使用super引用原始成员。

装饰器在ECMAScript Next / TypeScript中具有简洁的语法:

@withLogger
class YaLog {
  ...
}

由于装饰器基本上是辅助函数,因此它可以以较少表达的方式直接应用于ES6:

const YaLog = withLogger(class YaLog {
  ...
});

答案 2 :(得分:1)

您可以使用继承。创建新类Y并从数组中添加方法。然后,您可以使用Y扩展原始类(或任何其他类):

class Y {}
const methods = ['a', 'b', 'c']
methods.forEach(elem => {
  Y.prototype[elem] = () => {
    console.log('called with : ' + elem)
  }
})

class X extends Y {
  someUnrelatedFunction() {}
}

const x1 = new X();
x1.a();

我不确定您的用例是什么,但您可以创建一个扩展原始类的辅助函数。例如,这也可以起作用:

function extendWithMethods(methodNames, generalHandler) {
  class Y {}
  methodNames.forEach(elem => {
    Y.prototype[elem] = () => generalHandler(elem)
  })
  return Y
}

class X extends extendWithMethods(['a', 'b', 'c'], methodName => {
  console.log('called with : ' + methodName)
}) {
  someUnrelatedFunction() {}
}

const x1 = new X()
x1.a()