TypeScript 动态或以编程方式链接函数

TypeScript 函数链接,但我想以编程方式链接它们。

示例类:chain.ts

class MyChain {

  value: number = 0;

  constructor() {

    this.value = 0;

  }


  sum(args: number[]) {

    this.value = args.reduce((s, c) => s + c, 0);

    return this;

  }


  add(v: number) {

    this.value = this.value + v;

    return this;

  }


  subtract(v: number) {

    this.value = this.value - v;

    return this;

  }

}


const mc = new MyChain();

console.log(mc.sum([1, 2, 3, 4]).subtract(5).value);

5我在控制台上看到了这个数字。

现在,我对 JavaScript 和 TypeScript 还很陌生,所以我发现这个类中的函数实际上是该类实例数组的元素。因此,我可以这样做:

console.log(mc["sum"]([1, 2, 3, 4]).value);

这确实返回了数字10

现在,我对如何以编程方式链接它感到困惑。例如(无论如何,这显然不是我想做的,并且表明我对 JavaScript 缺乏理解:

console.log(mc["sum"]([1, 2, 3, 4]).mc["subtract"](5).value);

错误:

类型“MyChain”上不存在属性“mc”。ts(2339)

好吧,老实说,我直觉地知道这是行不通的。但是,想一想,我将如何以任何合理的语言访问多维数组的元素?

console.log(mc["sum"]([1, 2, 3, 4])["subtract"](5).value);

答对了。这可以解决问题。但是,这并不是我真正需要的解决方案。我需要的是这样的:

interface IChainObject {

  action: string;

  operand: number | number[];

}


const chainObj: IChainObject[] = [

  { action: "sum", operand: [1, 2, 3, 4] },

  { action: "subtract", operand: 5 },

];

首先,我想尝试一下:

console.log(mc[chainObj[0].action](chainObj[0].operand).value);

因此,生成一种机制,最终构建如下内容:

console.log(

  mc[chainObj[0].action](chainObj[0].operand)[chainObj[1].action](

    chainObj[1].operand

  ).value

);

因此,在我看来,我想要的是某种方式来生成它:

[chainObj[0].action](chainObj[0].operand)[chainObj[1].action](chainObj[1].operand)

由此,我的链对象具有一个或多个操作/操作数对象集:

const chainObj: IChainObject[] = [

  { action: "sum", operand: [1, 2, 3, 4] },

  { action: "subtract", operand: 5 },

];

现在,这就是我的大脑或多或少关闭的地方。我认为我需要生成一串字符串值,但它们只是字符串,并且不会真正按照我想要的方式作为函数的数组索引。

我为什么要这样做?最终,我想从 JSON 对象构建一个复杂的 Yup 模式对象。


慕莱坞森
浏览 156回答 3
3回答

PIPIONE

让我们从我发现的第一个误解开始:现在,我对 JavaScript 和 TypeScript 还很陌生,所以我发现这个类中的函数实际上是该类实例数组的元素。不是这种情况。Javascript 中的方括号用于所有属性查找,而不仅仅是数组索引。x.foo实际上相当于x["foo"], 并且相同的语法适用于数组,因为数组只是对象。Javascript 中的类只是具有原型属性的对象,原型属性本身就是一个对象。它包含默认属性列表,如果您实例化一个类并查找对象中不存在的属性,它将在原型中搜索它。那么,看一下代码:mc["sum"]([1, 2, 3])它在 中搜索“sum”属性mc,但找不到任何属性,因为您尚未定义该属性,因此它在 of 中搜索prototype,MyChain并找到该mc方法。因此,mc["sum"]就是sum的方法mc。现在,这段代码:console.log(mc["sum"]([1, 2, 3, 4]).mc["subtract"](5).value);不起作用,而且看起来很不对劲是有原因的。mc["sum"]([1, 2, 3, 4])返回mc,那么为什么您必须访问该mc属性(并不是该mc属性甚至存在)?subtract这就是为什么你的第二个例子(直接调用的例子)有效:console.log(mc["sum"]([1, 2, 3, 4])["subtract"](5).value);现在,让我们看看工作代码:const mc = new MyChain();interface IChainObject {  action: string;  operand: number | number[];}const chainObj: IChainObject[] = [  { action: "sum", operand: [1, 2, 3, 4, 5] },  { action: "subtract", operand: 5 },];let myChain = {};chainObj.forEach((o) => {  myChain = mc[o.action](o.operand);});console.log("myChain is", myChain["value"]);实际上您不需要很多这样的代码。它可以简化为:const mc = new MyChain();interface IChainObject {  action: keyof MyChain;  operand: number | number[];}const chainObj: IChainObject[] = [  { action: "sum", operand: [1, 2, 3, 4, 5] },  { action: "subtract", operand: 5 },];chainObj.forEach((o) => {  // bypass typescript type checking with cast  (mc[o.action] as Function)(o.operand);});console.log("myChain is", mc.value);本质上,按顺序forEach循环遍历元素chainObj。元素的值存储在变量 中o。mc[o.action]获取存储在 中的方法名称o.action,并使用方括号访问它。这基本上就是查方法了。然后,该方法被调用(o.operand)(在Javascript中,函数只是值,你可以像函数一样调用任何值,但如果它不是函数,则会出错)。mc然后修改自身,然后继续下一个循环。如果我们debugger在函数中插入一条语句,然后在第一个循环中中断,我们可以检查变量:正如您所看到的,该值从 0 开始,o.action是“sum”,并且mc[o.action]是 sum 方法。然后我们可以使用 调用 sum 方法o.operand,它将元素相加并将值设置为 15。然后,在第二个循环中:mc[o.action]是减法,我们用 来调用它o.operand,即 5,将值降低到 10。

汪汪一只猫

Javascript 中的大多数东西classes基本上都是objects. 1这意味着可以通过点符号或方括号符号来访问属性(或者在本例中为函数) 。让我们看一个可能有助于解释的示例:class MyClass {  myFunction(x) {    console.log(x);  }}const x = new MyClass();// attribute accessed via the dot notationx.myFunction("Hello World!");// attribute accessed via the bracket notation and a string x['myFunction']("Hello World, again!");// attribute accessed via a variable that is a string const functionName = 'myFunction';x[functionName]("Well uh, Hello World again?");// attribute accessed via a variable that is a string, and passing in an argumentconst argument = "This is " + "an argument";x[functionName](argument);为了进一步说明这一点:class MyClass {  myFunction(x) {    console.log(x);  }}const x = new MyClass();console.log(x.myFunction) // returns a functionconsole.log(x["myFunction"]) // returns a function// executing the functionx.myFunction("Method One");x["myFunction"]("Method Two")我们可以看到返回的函数可以被调用。让我们回到你的例子chainObj.forEach((o) => {   myChain = mc[o.action](o.operand); });o.action是函数名o.operand因此,is 大致翻译为:chainObj.forEach((o) => {   myChain = mc[functionName](arugment); });就像我们之前的例子一样。1 “类基本上只是对象”

紫衣仙女

这方面的内容有很多;我只想关注“让代码正常工作的秘诀是什么forEach()? ”“秘密”在于 的实例MyChain有一个名为 的属性value,该属性在调用每个方法后都会更新。代码并forEach()没有真正将调用链接在一起;它只是对每次命名的原始MyChain变量进行操作。mcMyChain由于该更新的所有方法this.value也返回this,因此您是否真的链接调用(对每个方法调用的返回值进行操作)并不重要:const chaining = new MyChain();console.log(chaining.add(3).subtract(1).value); // 2或者如果您只是连续调用原始对象上的方法:const notChaining = new MyChain();notChaining.add(3);notChaining.subtract(1);console.log(notChaining.value) // 2如果您希望它们之间存在差异,您可以通过制作两个版本来显示它MyChain;一种只能通过链接起作用,一种只能连续起作用。以下内容需要链接,因为它永远不会更新原始对象,并且方法调用会返回带有方法调用结果的新对象:class RealChain {  constructor(public value: number = 0) { }  sum(args: number[]) {    return new RealChain(args.reduce((s, c) => s + c, 0));  }  add(v: number) {    return new RealChain(this.value + v);  }  subtract(v: number) {    return new RealChain(this.value - v);  }}    const realChaining = new RealChain();console.log(realChaining.add(3).subtract(1).value); // 2const notRealChaining = new RealChain();notRealChaining.add(3);notRealChaining.subtract(1);console.log(notRealChaining.value) // 0并且以下内容禁止链接,因为它只更新原始对象并且其方法不返回任何内容:class NotChain {  value: number = 0;  constructor() {    this.value = 0;  }  sum(args: number[]) {    this.value = args.reduce((s, c) => s + c, 0);  }  add(v: number) {    this.value = this.value + v;  }  subtract(v: number) {    this.value = this.value - v;  }}const realNotChaining = new NotChain();realNotChaining.add(3);realNotChaining.subtract(1);console.log(realNotChaining.value) // 2const badNotChaining = new NotChain();console.log(badNotChaining.add(3).subtract(1).value); // error! // badNotChaining.add(3) is undefined so you can't call subtract() on it代码forEach()仅适用于NotChain实例,不适用于RealChain实例。如果您想要一个类似编程循环的东西,它实际上可以与链接一起使用,而不是在原始对象上调用方法,那么您可能应该使用reduce()而不是forEach():const realChainReduced = chainObj.reduce(  (mc, o) => mc[o.action](o.operand),   new RealChain() // or MyChain, doesn't matter);console.log("realChainReduced is", realChainReduced.value); // 10请注意,我没有介绍任何其他部分,包括 TypeScript 细节(此处使用的类型会产生一些编译器错误),因此请注意。Playground 代码链接
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

JavaScript