如何以编程方式实现TypeScript函数链式调用?
Great question—let’s break down exactly why this works under the hood in JavaScript, using your code as a guide.
1. The Core of Chaining: Methods Return this
First, look at every method in your MyChain class:
sum(args: number[]) { this.value = args.reduce((s, c) => s + c, 0); return this; // <-- This is the magic! } add(v: number) { this.value = this.value + v; return this; } subtract(v: number) { this.value = this.value - v; return this; }
When you call any of these methods on a MyChain instance (like mc.sum([1,2,3])), two critical things happen:
- The method updates the instance’s
valueproperty directly (it modifies the object’s state in place). - It returns a reference to the exact same instance via
this.
This is why manual chaining (mc.sum(...).subtract(...)) works: each method hands you back the same object, so you can immediately call another method on it.
2. What’s Actually Happening in Your forEach Loop
Let’s assume you started with an instance like this:
const mc = new MyChain();
Then you loop through your chainObj array:
chainObj.forEach(o => { mc = mc[o.action](o.operand); });
You might wonder why this doesn’t "overwrite" the instance each time—here’s the breakdown:
mc[o.action](o.operand)calls the method (e.g.,sumorsubtract) on the currentmcinstance.- That method modifies the instance’s
valueand returns the same instance (not a new object). - Assigning
mc = ...just reassigns the variable to point to the same object it already referenced. In fact, you could even omit the assignment entirely (mc[o.action](o.operand);) and get the exact same result—since the method is modifying the instance’s state directly.
3. JavaScript Object References: The Big Picture
JavaScript uses reference semantics for objects (including class instances). When you create const mc = new MyChain(), mc doesn’t store the object itself—it stores a reference to the object in memory.
When your method returns this, it’s returning that same reference. So every time you call a method on mc, you’re operating on the exact same underlying object. The loop is just automating the sequence of method calls, rather than writing them out manually like mc.sum(...).subtract(...).
4. Why Square Bracket Syntax Works
Your use of mc[o.action] is just dynamic method access—instead of hardcoding the method name (like mc.sum), you’re using the string from your action property to look up the method on the instance. This behaves exactly the same as calling the method directly; the only difference is how you reference the method name.
Step-by-Step Walkthrough of Your Example
Let’s trace through your chainObj array to see it in action:
- First iteration:
action: "sum", operand: [1,2,3,4,5]mc["sum"]([1,2,3,4,5])calls thesummethod on themcinstance.- The method calculates the total (15) and sets
mc.value = 15. - It returns the same
mcinstance. mc = mcdoes nothing (same reference), but the instance’s state is now updated.
- Second iteration:
action: "subtract", operand: 5mc["subtract"](5)calls thesubtractmethod on the same instance.- It sets
mc.value = 15 - 5 = 10. - Returns the same instance again.
At the end, mc.value is 10—exactly what you’d get from manual chaining: mc.sum([1,2,3,4,5]).subtract(5).value.
Quick Refactor Tip
Since the assignment mc = ... is redundant (you’re just reassigning to the same reference), you can simplify the loop to:
chainObj.forEach(o => mc[o.action](o.operand));
This will produce the exact same result, as you’re directly modifying the instance’s state with each method call.
内容的提问来源于stack exchange,提问作者Dan7el

