十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

uni-app x UTS 语言循环语法全解析:for、for...of、for...in、while 与 break/continue 的多端一致性实践

uni-app x UTS 语言循环语法全解析:for、for...of、for...in、while 与 break/continue 的多端一致性实践 uni-app x UTS 语言循环语法全解析for、for...of、for...in、while 与 break/continue 的多端一致性实践【免费下载链接】uni-appA cross-platform framework using Vue.js项目地址: https://gitcode.com/gh_mirrors/un/uni-app导读UTS 是 uni-app x 使用的类 TypeScript 语言用于同时编译到 Web、AndroidKotlin、iOSSwift与 HarmonyOS 等多个平台。由于各平台底层语法与迭代协议的差异循环语句尤其是 for...of 与 for...in在不同端上会表现出截然不同的行为。本文以 docs/uts/loops.md 为核心系统讲解 UTS 中 for、for...of、for...in、while、do...while 以及 break / continue 的完整语法、执行流程与平台差异并结合仓库中的 可迭代协议文档、可枚举与可迭代差异文档 及示例工程源码给出可直接复制运行的代码与避坑指南。一、for 循环经典计数循环一个 for 循环会一直重复执行直到指定的循环条件为false。UTS 中 for 语句的语法与 TypeScript / JavaScript 完全一致for ([initialExpression]; [condition]; [incrementExpression]) { statement; }当一个 for 循环执行时会依次发生以下过程初始化如果有初始化表达式initialExpression它将被执行。该表达式通常用来初始化一个或多个循环计数器。条件判断计算condition表达式的值。如果condition的值是true则执行循环体中的语句如果condition的值是falsefor 循环终止。如果condition表达式整个被省略则其值会被认为是true形成死循环需配合 break 退出。执行语句循环体中的statement被执行。如果需要执行多条语句可以使用块{ ... }包裹。更新表达式如果有更新表达式incrementExpression执行它通常是计数器自增/自减。回到步骤 2继续判断条件直到条件为false。基础示例for (let i 0; i 10; i) { // 循环体i 从 0 递增到 9 }for 循环在三个平台上的行为与 Web 标准一致是跨端最安全的计数循环写法。在 UTS 中let i 0是循环变量的推荐声明方式i与i 1均可作为更新表达式使用。实战参考响应式数据中的 for 循环在示例工程 examples/hello-uvue/pages/reactivity/core/reactive/reactive.uvue 中可以看到 for...of 与 forEach 在 uni-app x 响应式reactivewatchEffect场景下的真实用法开发者用for (const item of map3)遍历reactive(new Map(...))用set2.forEach(...)与for (const item of set3)遍历响应式 Set并把遍历行为放入watchEffect中以追踪响应式依赖。这说明 UTS 的循环语法可以直接作用于 uni-app x 的响应式集合对象是页面开发中的高频模式。二、for...of全端行为一致的可迭代遍历for...of 语句执行一个循环该循环处理来自可迭代对象的值序列。可迭代对象包括内置对象的实例例如 Array、String、Map、Set还包括用户自定义的可迭代对象。for (variable of iterable) statementfor...of 循环按顺序逐个处理从可迭代对象获取的值。循环对值的每次操作被称为一次迭代而循环本身被称为迭代可迭代对象。每次迭代都会执行可能引用当前序列值的语句。版本前提在HBuilderX 4.41之前UTS 并未对 for...of 做特殊处理在 Android / iOS 上直接调用会编译报错、无法使用。从HBuilderX 4.41开始UTS 以 Web 的可迭代协议 为标准实现了可迭代协议for...of 的全端执行效果与 Web 标准一致详见 docs/uts/iterable.md。1. Set / Array遍历内部值类似 Set / Array 这样的一维数据容器for...of 会遍历其内部属性值let u1 new Setany() u1.add(111) u1.add(222) for (item of u1) { console.log(item,item) }执行结果 item 111 item 222同样数组可以直接遍历let a1 [111,222,false] for(perItem of a1){ console.log(perItem) }该代码在 HBuilderX 4.41 之前版本在 Android / iOS 会编译报错在 4.41 之后全端输出111 222 false示例工程 examples/hello-uts/uni_modules/uts-tests/utssdk/Date.uts 中UTS 单元测试也大量使用for(let perDate of dateList)、for(let dateStr of dateStrings)来遍历日期对象与字符串数组可作为真实可运行的参考。2. Map遍历键值对封装为数组Map 的 for...of 遍历会将其 key、value 封装为一个新的 Array 数组返回let u1 new Mapstring,any() u1.put(aaa,111) u1.put(bbb,222) for (item of u1) { console.log(item,item) }执行结果 [Array] [ aaa, 111 ] [Array] [ bbb, 222 ]即每次迭代拿到的item是一个二元数组item[0]为 key、item[1]为 value与 Web 上遍历 Map 的条目数组entries行为一致。3. string逐字符遍历for...of 会遍历 string 的每一个字符let a 123456 for(item of a ){ console.log(item) }执行结果 1 2 3 4 5 64. 不支持 for...of 的类型编译报错number/Date/RegExp/Error/UTSJSONObject/自定义的 type 类型/自定义 class默认不支持 for...of直接使用会编译报错For-loop range must have an iterator() method需要特别说明的是一般情况下自定义的 type 类型 / 自定义 class 如果直接使用 for...of 会运行报错但有些场景我们需要赋予自定义 class 循环访问自身的能力甚至要定制迭代访问的内容实现方式详见 docs/uts/iterable.md 中的UTSValueIterable接口。5. 进阶通过 UTSValueIterable 定制自定义 class 的迭代自 HBuilderX 4.41 起UTS 语言内置了UTSValueIterable接口用于支持自定义对象的可迭代规则。当开发者手动实现该接口时就可以定制实例对象在 for...of 运算下的规则详见 docs/uts/iterable.md。例如一个持有数组但默认不可迭代的 classclass TestClass { holderArray: (any | null)[] [11, 22, null, 33, 44, null] } let test new TestClass() for (item of test) { console.log(item,item) }直接遍历会触发编译报错error: For-loop range must have an iterator() method。通过实现UTSValueIterable接口即可让其支持 for...ofclass TestClass implements UTSValueIterableany | null { holderArray: (any | null)[] [11, 22, null, 33, 44, null] valueIterator(): UTSIteratorany | null { let holderIndex 0; let obj: UTSIteratorany | null { next: () : UTSIteratorResultany | null { const done holderIndex this.holderArray.length return { done, value: done ? null : this.holderArray[holderIndex], } as UTSIteratorResultany | null } } return obj } }执行结果item [number] 11 item [number] 22 item null item [number] 33 item [number] 44 item null其工作原理为UTSValueIterable接口仅存在一个待实现函数valueIterator返回值是UTSIterator类型与 Web 中的 Iterator 类似开发者需要实现UTSIterator中的next函数next返回一个UTSIteratorResult数据结构包含done与value两个字段与 Web 的迭代器协议一样done决定是否可以继续迭代value对应当前迭代的具体数值。更进一步如果希望 for...of 时仅遍历出非空的值可以在valueIterator中先过滤再迭代class TestClass implements UTSValueIterableany | null { holderArray: (any | null)[] [11, 22, null, 33, 44, null] valueIterator(): UTSIteratorany | null { let holderIndex 0; let arr this.holderArray.filter((value) { return value ! null }) let obj: UTSIteratorany | null { next: () : UTSIteratorResultany | null { const done holderIndex arr.length return { done, value: done ? null : arr[holderIndex], } as UTSIteratorResultany | null } } return obj } }执行结果null 被过滤item [number] 11 item [number] 22 item [number] 33 item [number] 44三、for...in平台差异大集合遍历不推荐UTS 语言目前并没有对 for...in 进行特殊处理开发者在 UTS 中使用 for...in 语法时会触发各平台具体的内置规则。可以运行但执行效果与 Web 存在一定的差异。核心建议为了保持与 Web 行为的一致性针对 Array / Set / Map 等集合类型以及 String 的遍历建议使用 for...of不建议使用 for...in。下面是 UTS 内置对象对 for...in 的支持情况一览类型WebAndroidiOSArray返回索引返回数值返回数值Set不可枚举返回数值返回数值Map不可枚举返回键值对返回键值对String返回索引返回字符返回字符自定义 type返回属性名返回属性名返回属性名UTSJSONObject返回属性名返回属性名返回属性名平台差异的底层原因根据 docs/uts/enumerability_iterable.md平台差异源于各端语言对可迭代与可枚举两种协议的支持情况不同平台可迭代可枚举web支持ECMAScript-2015支持ECMAScript-1999kotlin支持不支持swift支持不支持平台可迭代可枚举webfor..offor..inkotlinfor..in不支持swiftfor..in不支持也就是说原生开发场景下 Kotlin 和 Swift 均只支持 for...in 语法、不支持 for...of 语法但原生 for...in 的用法与 Web 存在差异——它实际对应的是 Web 中的可迭代概念。UTS 的 for...of 正是把 Web 的可迭代语义统一到了各端之上。来看一个典型的差异示例。同样的代码let a1 [111,222,false] for(perItem in a1){ console.log(perItem,perItem) }Web 执行结果遍历的是数组下标perItem 0 perItem 1 perItem 2Android 执行结果遍历的是数组元素perItem [number] 111 perItem 222 perItem [boolean] false同一段 for...in 代码在 Web 上输出下标、在 Android 上输出元素值这正是 docs/uts/loops.md 反复强调集合遍历不推荐使用 for...in的原因。此外还需要注意UTS 语言尚不支持对可枚举协议进行自定义与可迭代协议不同因此 for...in 的平台差异在 HBuilderX 4.41 之后仍会保留。四、while 循环先判断后执行一个 while 语句只要指定的条件求值为真true就会一直执行它的语句块。一个 while 语句看起来像这样while (condition) { statement; }执行规则如果条件变为假false循环里的statement将会停止执行并把控制权交回给 while 语句后面的代码条件检测会在每次 statement 执行之前发生如果条件返回为真statement被执行随后再次测试条件如果条件返回为假执行停止并交回控制权要执行多条语句语句块要使用语句块{ ... }包裹。经典示例计算 1 2 3let n 0; let x 0; while (n 3) { n; x n; } // 结束时 n 3x 6while 也是替代在 forEach 回调中修改数组这一危险操作的安全写法见下文第六节。五、do...while 循环至少执行一次do...while 语句一直重复直到指定的条件求值得到假值false。一个 do...while 语句看起来像这样do { statement; } while (condition);执行规则statement在检查条件之前会执行一次因此无论条件是否成立循环体至少会执行一次要执行多条语句语句块要使用块语句{ ... }包裹如果condition为真truestatement将再次执行在每个执行结尾会进行条件检查当condition为假false执行停止并把控制权交回给 do...while 后面的语句。示例let i 0; do { i 1; } while (i 10); // 结束时 i 10六、forEach集合遍历的补充手段除了循环语法部分内置对象还支持 forEach 方法可以更简单地遍历数组的 forEachforEach(callbackfn, thisArg?)对数组的每个元素执行一次给定函数回调参数为(value: T, index: number, array: ArrayT) void返回void。示例const items: string[] [item1, item2, item3]; const copyItems: string[] []; items.forEach((item: string) { copyItems.push(item); }); console.log(copyItems) // [item1, item2, item3]Set 的 forEach回调接收三个参数(value, value2, set)由于 Set 没有键value 会同时传给前两个参数。示例const set1 new Setnumber(); set1.add(42); set1.add(42); // 重复值会被去重 set1.add(13); set1.forEach((item) { console.log(item); // 输出 42、13 })forEach 的强制注意事项来自 docs/uts/buildin-object-api/array.md不可在 forEach 的callbackFn里添加或删除原数组元素此行为是危险的——在 Android 平台会造成闪退在 iOS 平台会造成行为不符合预期例如array.pop()在 iOS 会输出 a,b,c而 JS 会输出 a,b。如果确实需要边遍历边修改数组请改用 while 循环array [a, b, c]; let index 0; while (index array.length) { console.log(array[index]); // 需要增删元素时在 while 中自行控制 index 与数组长度 index; }七、break终止整个循环使用break语句来终止循环。在 for、do、while 等各种循环中以及在 switch 中都可以使用 break 跳出循环或 switch 的选择。示例一在 for 循环中使用 breakfor (let i 0; i 10; i) { if (i 5) { console.log(i) break; // 如果这里有代码的话也不会执行因为 break 执行时已经跳出了 for 循环。 } } // break 后跳出 for 循环开始向下执行这里的代码。 // 输出 6示例二在 while 循环中使用 break用于终止条件恒真的循环let x 0; while (true) { x; if (x 5) { break; } } // break 跳出后x 6注意while (true)本身是死循环必须依靠 break 才能退出这是省略 for 循环 condition 之外的另一种常见无限循环 条件退出模式。八、continue跳过本次迭代使用continue语句来终止当前循环。与 break 不同continue 只是终止了这一次循环还会在下一次迭代时继续执行循环。continue 可以在 for、do、while 等各种循环中使用。示例一在 for 循环中使用 continuefor (let i 0; i 10; i) { if (i 5) { console.log(i) continue; // continue 后本次循环结束i1 后开始下一次循环。 // 这里的代码不会被执行因为上一行 continue 了 } } // 输出 6、7、8、9示例二在 while 循环中使用 continuelet x 0; while (true) { x; if (x 5) { continue; } } // continue 只是跳过后续语句while (true) 仍会继续执行需配合 break 使用才可退出九、总结UTS 循环选型建议场景推荐语法原因需要下标、需要精确控制步长for全端行为一致语义明确遍历 Array / Set / String 的值for...ofHBuilderX 4.41 起全端与 Web 一致遍历 Map 的键值对for...of每次迭代得到[key, value]数组遍历自定义 class 内部元素for...of UTSValueIterable可定制迭代内容与规则遍历集合但想用回调forEach注意不可在回调中增删原数组需要先判断后执行while条件在循环体之前检测需要至少执行一次do...while循环体先执行再判断条件对 Array / Set / Map / String 用 for...in❌ 不推荐各平台返回索引/数值/键值对不一致行为与 Web 有差异提前退出 / 跳过本次break / continue适用于 for、while、do...while 与 switch如果希望在 uni-app x 的跨端Web / Android / iOS / HarmonyOS项目中写出行为完全一致的遍历代码请遵循三条原则集合与字符串优先用 for...of需要自定义对象遍历时实现UTSValueIterable接口避免对集合使用 for...in。相关详细协议说明可进一步阅读 docs/uts/iterable.md 与 docs/uts/enumerability_iterable.md内置对象的 forEach 完整参数与兼容性版本见 docs/uts/buildin-object-api/array.md 与 docs/uts/buildin-object-api/set.md可运行的示例代码可在 examples/hello-uvue 与 examples/hello-uts 工程中继续查阅。【免费下载链接】uni-appA cross-platform framework using Vue.js项目地址: https://gitcode.com/gh_mirrors/un/uni-app创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表