ArkTS 进阶之道(4):箭头函数的 this 是啥?词法绑定 vs function 表达式
ArkTS 进阶之道4箭头函数的 this 是啥词法绑定 vs function 表达式本文是「ArkTS 进阶之道」系列第 4 篇开「ArkTS 作用域哲学」新阶段。上三篇讲「ArkTS 类型哲学」三个逃逸点any 篇 50 /{}篇 51 / 解构 篇 52——都是推断链断裂。本文换角度讲作用域function 表达式为啥被禁——根因在this绑定机制function 表达式是「动态 this」的逃逸点箭头函数是「词法 this」的显式锁定。报错速查篇 43 讲过arkts-no-func-expressions怎么改本文讲为哈要这么改——根因在作用域哲学。一、开篇function 表达式不是轻量的语法糖是 this 绑定的逃逸点你写 TypeScript 时function 表达式是「轻量的语法糖」要函数就写function// TS 里 function 表达式咧装 const fn function(x: number): number { return x * 2 } fn(10) ← 返 20 // this 绑定看调用方 class Counter { count 0 inc() { return function() { this.count } ← function 表达式this 动态绑定 } } const c new Counter() const inc c.inc() inc() ← TypeErrorthis 是 undefinedstrict 模式count 没加你写鸿蒙 ArkTS 时同一行编译期就炸// ArkTS 里 function 表达式编译期就拦 const fn function(x: number): number { return x * 2 } ← ERROR: 10605046 arkts-no-func-expressions报错原文ERROR: 10605046 ArkTS Compiler Error Error Message: Use arrow functions instead of function expressions (arkts-no-func-expressions). At File: xxx.ets:9:16糖和逃逸点的区别TS 把 function 表达式当「轻量语法糖」你懒得写箭头就写 functionArkTS 把 function 表达式当「this 绑定的逃逸点」你偷懒编译器就拦。根因跟类型逃逸点一样——作用域链在 function 表达式上断掉编译器无法静态确定this。二、根因function 表达式的动态 this 逃逸性鸿蒙 ArkTS 的 function 表达式是this 绑定的逃逸点——this动态绑定看调用方编译器无法静态确定来自三重作用域约束。约束 1function 表达式的 this 动态绑定——作用域链断裂ArkTS 要求每个标识符显式可静态确定this 词法绑定是核心约束。function 表达式的this是「动态绑定」看调用方// ✅ this 词法绑定箭头函数捕获外层 this class MyCounter { count: number 0 inc(): () void { return (): void { this.count } ← 箭头函数this 词法绑定 MyCounter 实例 } } const c new MyCounter() const inc c.inc() inc() ← this ccount 成 1静态可确定 // ❌ this 动态绑定function 表达式看调用方 class MyCounter { count: number 0 inc(): () void { return function() { this.count } ← function 表达式this 动态绑定看调用方 } } const c new MyCounter() const inc c.inc() inc() ← this undefinedstrictcount 没加静态不可确定 inc.call(other) ← this othercount 加到 other 上静态不可确定词法 vs 动态箭头函数() 的this词法绑定编译期就知道this是MyCounter实例function 表达式的this动态绑定看运行时调用方编译器静态不可确定。ArkTS 要求词法绑定不要动态逃逸。约束 2function 表达式的 this 追踪失效——单态化要追调用方ArkTS 走静态单态化优化——每个this类型编译期生成一份专用代码。function 表达式的this是「动态的」编译器要先追调用方定类型再单态化多了一层// ✅ 单态化直接箭头函数 this 词法锁定 class MyCounter { count: number 0 inc(): () void { return (): void { this.count } ← this MyCounter 单态代码直接生成 } } // ❌ 单态化要追调用方function 表达式先定 this 再单态 class MyCounter { count: number 0 inc(): () void { return function() { this.count } ← 先追调用方定 this 类型再单态化 } }function 表达式的this要编译器先追调用方定类型inc()调用方是c还是other再单态化多一层追踪开销。禁掉逃逸点单态化直接生效——鸿蒙跑在手机/手表/车机上每个追踪开销都抠。约束 3function 表达式与装饰器体系不兼容ArkUI 的状态装饰器要「this 静态确定」做依赖追踪。function 表达式的动态this装饰器感知不到// ✅ this 静态确定依赖追踪正常 Entry Component struct Index { State count: number 0 build() { Button(点我) .onClick(() { this.count }) ← 箭头函数this Index 实例依赖追踪正常 } } // 点按钮 → this.count → UI 自动刷新装饰器追 this.count // ❌ this 动态绑定依赖追踪失效 Entry Component struct Index { State count: number 0 build() { Button(点我) .onClick(function() { this.count }) ← function 表达式this 动态看调用方 } } // 点按钮 → this 可能不是 Index 实例 → UI 不刷新装饰器感知不到 this.countfunction 表达式的this动态看调用方onClick 的调用方可能是 Button 实例不是 Index装饰器追不到this.countUI 不刷新。禁掉逃逸点状态追踪边界保持「this 静态确定」的清晰。三、真机配图箭头函数替代 function 表达式正解能编译能跑替代 function 表达式正解初始态doubleArrow/MyCounter/inc/applyTwice 均未调用点调三种替代后doubleArrow20、Counter2、applyTwice11 均真返了正确值报错写法function 表达式编译就炸装不上真机正解写法箭头函数 / 词法绑定 this / 箭头回调 替代能跑三种替代均真返了正确值。function 表达式编译就炸改回箭头函数就跑——这是 ArkTS this 词法绑定约束最直白的证据。四、真解法三招替代 function 表达式解法 1箭头函数替替代 function 表达式90% 场景首选// ✅ 箭头函数替替代 function 表达式 function doubleArrow(): (x: number) number { return (x: number): number x * 2 ← 箭头函数this 词法绑定返函数不要 function 表达式 } // 调用 const fn doubleArrow() console.info(${fn(10)}) ← 输出 20为哈能跑用(x: number): number x * 2箭头函数替代function(x: number): number { return x * 2 }this 词法绑定箭头函数无自己的 this用外层三重约束全满足——作用域链完整、单态化直接生效、依赖追踪正常。首选这个90% 的场景箭头函数就够。解法 2箭头函数捕获 this 词法绑定要闭包时// ✅ 箭头函数捕获 this词法绑定不要 function 表达式 class MyCounter { count: number 0 inc(): () void { return (): void { this.count } ← 箭头捕获 this MyCounter 实例词法绑定 } } // 调用 const c new MyCounter() const incFn c.inc() incFn() incFn() console.info(${c.count}) ← 输出 2为哈能跑箭头函数的this词法绑定编译期就锁定MyCounter实例incFn()调用方变也不影响this。要写「函数闭包捕获实例状态」时用这个——比 function 表达式安全this静态可确定。解法 3箭头函数回调替代 function 表达式回调要高阶函数时// ✅ 箭头函数回调替代 function 表达式回调 function applyTwice(value: number, op: (x: number) number): number { return op(op(value)) ← 高阶函数参是箭头函数不要 function 表达式 } // 调用 const r: number applyTwice(5, (x: number): number x 3) console.info(${r}) ← 输出 11(53)311为哈能跑高阶函数的回调参用箭头函数(x: number): number x 3替代function(x) { return x 3 }回调内this词法绑定。要写「函数接函数参」时用这个——比 function 表达式回调更安全回调内this静态可确定。五、一句话哲学function 表达式是 this 绑定的逃逸点不是轻量的语法糖。ArkTS 的三重作用域约束——this 词法绑定编译期静态确定、静态单态化直接生效不要追调用方、装饰器依赖追踪this 静态才刷新 UI。function 表达式的动态this把这三重都打断所以编译器编译期就拦。替代方案就三个箭头函数替替代首选、箭头函数捕获 this要闭包、箭头函数回调要高阶函数。类型 vs 作用域类型哲学三个逃逸点any/{}/解构讲「推断链断裂」作用域哲学本篇讲「this 绑定逃逸」——都是「偷懒的糖」变「静态确定的断点」根因一样编译器要静态确定解法都是「显式词法绑定替代」。下一篇ArkTS 进阶之道5—— struct 里为啥不能嵌 struct终态声明 状态边界对应报错速查篇 45arkts-no-structural-nesting讲根因。报错速查回链报错码报错速查篇本文进阶点arkts-no-func-expressions篇 43function 表达式的动态 this 逃逸性arkts-no-structural-nesting篇 45下篇预告struct 嵌套的终态声明约束真机 demo 完整代码// ✅ 正解 1箭头函数替代 function 表达式词法绑定 this function doubleArrow(): (x: number) number { return (x: number): number x * 2 } // ✅ 正解 2箭头函数捕获 this词法绑定不要 function 表达式 class MyCounter { count: number 0 // 箭头函数捕获 this词法绑定 inc(): () void { return (): void { this.count } } } // ✅ 正解 3箭头函数回调替代 function 表达式回调 function applyTwice(value: number, op: (x: number) number): number { return op(op(value)) } Entry Component struct Index { State resultA: string (未调用) State resultB: string (未调用) State resultC: string (未调用) State clicks: number 0 State log: string (未操作) build() { Column({ space: 12 }) { Text(篇 53 配图箭头函数替代 function 表达式正解) .fontSize(18).fontWeight(FontWeight.Bold).margin({ top: 20, bottom: 8 }) Text(function 表达式编译炸 → 箭头函数词法绑定 this替代) .fontSize(12).fontColor(#888).margin({ bottom: 16 }) Column({ space: 6 }) { Text(doubleArrow ${this.resultA}).fontSize(14) Text(Counter ${this.resultB}).fontSize(14) Text(applyTwice ${this.resultC}).fontSize(14) Text(clicks ${this.clicks}).fontSize(16) Text(日志${this.log}).fontSize(12).fontColor(#333).margin({ top: 4 }) } .width(92%).padding(12).backgroundColor(#f5f5f5).borderRadius(8) Button(调 doubleArrow箭头替替代 function) .width(92%).height(44).fontSize(14) .onClick(() { const fn doubleArrow() this.resultA String(fn(10)) this.clicks this.log 第 ${this.clicks} 次${this.resultA} }) Button(调 Counter箭头捕获 this) .width(92%).height(44).fontSize(14) .onClick(() { const c new MyCounter() const incFn c.inc() incFn() incFn() this.resultB String(c.count) this.clicks this.log 第 ${this.clicks} 次${this.resultB} }) Button(调 applyTwice箭头回调) .width(92%).height(44).fontSize(14) .onClick(() { this.resultC String(applyTwice(5, (x: number): number x 3)) this.clicks this.log 第 ${this.clicks} 次${this.resultC} }) } .width(100%).height(100%).alignItems(HorizontalAlign.Center) } }写鸿蒙 ArkTS 记住function 表达式const fn function(x) { return x * 2 }编译就炸——10605046 arkts-no-func-expressions Use arrow functions instead of function expressions。改回箭头函数(x: number): number x * 2首选、箭头函数捕获 this(): void { this.count }要闭包、箭头函数回调(x: number): number x 3要高阶函数三招都能跑。function 表达式是 this 绑定的逃逸点三重作用域约束全打断是根因箭头函数词法绑定是首选解法