3 回答
TA贡献1829条经验 获得超9个赞
让我们从我发现的第一个误解开始:
现在,我对 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。
TA贡献1898条经验 获得超8个赞
Javascript 中的大多数东西classes
基本上都是objects
. 1
这意味着可以通过点符号或方括号符号来访问属性(或者在本例中为函数) 。
让我们看一个可能有助于解释的示例:
class MyClass {
myFunction(x) {
console.log(x);
}
}
const x = new MyClass();
// attribute accessed via the dot notation
x.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 argument
const 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 function
console.log(x["myFunction"]) // returns a function
// executing the function
x.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 “类基本上只是对象”
TA贡献1839条经验 获得超15个赞
这方面的内容有很多;我只想关注“让代码正常工作的秘诀是什么forEach()? ”
“秘密”在于 的实例MyChain有一个名为 的属性value,该属性在调用每个方法后都会更新。代码并forEach()没有真正将调用链接在一起;它只是对每次命名的原始MyChain变量进行操作。mc
MyChain由于该更新的所有方法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); // 2
const 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) // 2
const 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 细节(此处使用的类型会产生一些编译器错误),因此请注意。
添加回答
举报