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
?,F在,這段代碼:
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 細節(此處使用的類型會產生一些編譯器錯誤),因此請注意。
添加回答
舉報