From ae099ee80769514f4702a6fa83251504fd848188 Mon Sep 17 00:00:00 2001 From: WOSHIMAHAIFENG Date: Tue, 12 Nov 2024 19:51:32 +0800 Subject: [PATCH] Update data-type.md --- docs/uts/data-type.md | 331 +++++++++++++++++++++++++++--------------- 1 file changed, 211 insertions(+), 120 deletions(-) diff --git a/docs/uts/data-type.md b/docs/uts/data-type.md index 2bfb1860..ce8dfd1d 100644 --- a/docs/uts/data-type.md +++ b/docs/uts/data-type.md @@ -1,6 +1,11 @@ +``` + +``` + # 类型@data-type 强类型语言的特点,是数据类型要求严格。它带来2个好处: + 1. 高性能:明确的类型有更大的优化空间,在Android等OS上可以节省内存、提高运算速度;web端由于仍编译为js,不具有类型性能优化。 2. 安全的代码:强类型代码编写虽然没有弱类型自由,但类型检查、非空检查...等各种检查可以提高代码的健壮性。 @@ -11,6 +16,7 @@ 比如 `"abc"`和`"你好"`,都属于字符串string,所有string类型有相同的方法、属性,比如`.length`属性获取字符串长度。 UTS 的类型有: + - 基础类型:boolean、number、string、any、null,都是小写。前3个typeof返回类型名称,null的typeof是object,any的typeof是运行时值的类型。 - 对象类型:Date、Array、Map、Set、UTSJSONObject,首字母大写。typeof返回"object",判断准确类型需使用 instanceof - 使用 type 来自定义类型 @@ -23,7 +29,6 @@ UTS 的类型有: 除了上述`运行时类型`,uts还有`开发时类型`的概念,指为了在开发期间ide可以更好的进行代码提示和校验,但在编译后这些类型会被擦除,变成`运行时类型`。详见[开发时类型](#devtype) 和 [类型擦除](#type-erasure) - ## 布尔值(boolean) 布尔是简单的基础类型,只有2个值:`true` 和 `false`。 @@ -36,6 +41,7 @@ c = true // 后续为变量赋值字面量 ``` **注意:** + - 在js里,true == 1、 false == 0。但在其他强类型语言里,`1`和`0`是数字类型,无法和布尔类型相比较。 - 注意 boolean 不要简写成 bool @@ -68,6 +74,7 @@ let d = 3.14159 //注意:目前版本推导d为float类型,新版本 请注意: number本身的使用很简单,但混入了平台专有数字类型后,会引出很多细节需要学习。 + - 如果您不调用原生API,初学uts时建议跳过本节,直接往下看string类型。 - 如果您是插件作者,那请务必仔细阅读本章节。 @@ -92,7 +99,6 @@ number本身的使用很简单,但混入了平台专有数字类型后,会 如果涉及大量运算,建议开发者不要使用 number、Int? ,要明确使用 Int等类型 [详情](https://kotlinlang.org/docs/numbers.html#numbers-representation-on-the-jvm) - #### Swift 专有的数字类型 @swiftnumber |类型名称 |长度 |最小值 |最大值 |描述| @@ -115,8 +121,8 @@ number本身的使用很简单,但混入了平台专有数字类型后,会 |Float64 |64bit |2.2250738585072014E-308 |1.7976931348623157E308 |双精度浮点型,等同Double| |Double |64bit |2.2250738585072014E-308 |1.7976931348623157E308 |双精度浮点型| - **注意:** + - Swift 中 Int 类型是根据平台动态的,在 32 位设备下等同于 Int32, 在64位设备下等同于 Int64。因此建议整型使用 Int, 除非必要,且在保证不会溢出的场景下才使用 Int32、Int64。 - 同样,Swift 中的 UInt 类型也是根据平台动态的,在 32 位设备下等同于 UInt32, 在64位设备下等同于 UInt64。建议使用 UInt,非必要不使用 UInt32、UInt64。 - Float16 在 iOS14.0 及以上系统上才能使用,使用时注意做系统版本号判断。[参考](https://uniapp.dcloud.net.cn/plugin/uts-uni-api.html#设备) @@ -132,7 +138,8 @@ js的专用数字类型是BigInt。[详见](https://developer.mozilla.org/zh-CN/ 使用下面的方法,虽然可能会被编辑器报语法错误(后续HBuilderX会修复这类误报),但编译到 kotlin 和 swift 运行是正常的。 - 声明特定的平台数字类型 - > 平台专有数字类型,均为首字母大写,注意与 number 首字母小写是不同的 + +> 平台专有数字类型,均为首字母大写,注意与 number 首字母小写是不同的 ```ts // #ifdef APP @@ -186,19 +193,23 @@ a 会被自动推导成什么类型?是Int、double、还是number?值是0 - 规则1. 在定义变量时,若没有显式声明变量类型,通过数字字面量以及数字字面量组成的运算表达式来给变量赋值,此时变量类型默认推导为 number类型 举例说明: + * HBuilderX3.9前,运行到App,由kotlin和swift编译器推导 + ```ts let a = 1 // 类型为Int let b = 1/10 // 类型为Int,值为0 ``` * HBuilderX3.9起,运行到App,未显式声明类型的变量,需根据数字字面量推导变量类型,此时由 uts 编译器推导,变量类型默认为 number + ```ts let a = 1 // 类型为number let b = 1/10 // 类型为number,值为0.1 ``` 如您已经显式声明变量类型,无需自动推导,则不受上述规则变化影响。不管HBuilderX 3.9之前还是之后,以下代码都是一样的 + ```ts let a:Int = 1 // 类型为Int ``` @@ -208,8 +219,9 @@ let a:Int = 1 // 类型为Int 但`let b:Int = 1/10` 会在 HBuilderX 3.9+起报错,原因见下面第二条规则。 再澄清下规则1: - * 如果定义变量时已经显式声明了类型,和规则1无关 - * 如果不是定义变量,和规则1无关 + +* 如果定义变量时已经显式声明了类型,和规则1无关 +* 如果不是定义变量,和规则1无关 也就是如下代码中,`60`这个字面量的处理,和规则1无关,不会把这个`60`改为number类型 @@ -223,6 +235,7 @@ test(60) // 这个60可以正常传入,无论HBuilderX 3.9之前还是之后 - 规则2. 纯数字字面量的除法,其结果会变成number 在HBuilderX 3.9以前,字面量除法也由kotlin和swift自动推导,kotlin下存在一个问题,看如下代码: + ```ts function test(score: number): boolean { return (score>=60) @@ -237,13 +250,15 @@ test(1/10) // 报错,类型不匹配。需要number而传入Int 引入这个规则后,上述代码就可以正常运行了。 这里的`纯数字字面量的除法`,指除法表达式中除了数字和运算符,不包括任何其他东西: + - 比如变量:`let a:Int=1;let b:Int= a/1` - 比如使用 as 断言:`(1 as Int)/10` -以上除法表达式,都不是“纯数字字面量的除法”,都不会被推导为number。 + 以上除法表达式,都不是“纯数字字面量的除法”,都不会被推导为number。 但是这条规则,也会导致一个**向下兼容问题**。 下面的代码在HBuilderX 3.9之前是可以正常运行的,但在3.9起会报错,因为1.0/10被转为了number类型,传入需要Double的函数时就会类型不匹配。 + ```ts function test(score: Double): boolean { return (score>=60.0) @@ -252,6 +267,7 @@ test(1.0/10) ``` 在HBuilderX 3.9后,为了正确传入Double,要注意跳过规则2。避免纯数字字面量除法,所以正确的写法是: + ```ts function test(score: Double): boolean { return (score>=60.0) @@ -271,19 +287,19 @@ test((1.0 as Double)/10) //表达式中任意一个数字as一下,都不会走 所有的 number 都支持下列方法进行转换(部分类库API使用java编写,其要求的java类型与下列kotlin类型完全一致,可以直接使用 - * toByte(): Byte - * toShort(): Short - * toInt(): Int - * toLong(): Long - * toFloat(): Float - * toDouble(): Double +* toByte(): Byte +* toShort(): Short +* toInt(): Int +* toLong(): Long +* toFloat(): Float +* toDouble(): Double 另外 number 还具备下列函数进行整型的无符号转换,这部分API 在jvm上没有对应的原始数据类型,主要的使用场景是 色值处理等专业计算场景的`多平台拉齐` - * toUByte(): UByte - * toUShort(): UShort - * toUInt(): UInt - * toULong(): ULong +* toUByte(): UByte +* toUShort(): UShort +* toUInt(): UInt +* toULong(): ULong ```ts let a:number = 3 @@ -300,6 +316,7 @@ i.toDouble() // 转换为 Double 类型 ``` 把 kotlin 专有数字类型,转换为number,使用Number.from()方法 + ```ts let a: Int = 1 let a1 = Number.from(a) // Int转number @@ -378,6 +395,7 @@ s3 = "abc" app-ios平台上原生有 NSString ,某些系统API或者三方库API可能使用NSString类型的字符串参数或者返回值。 定义NSString + ```ts let nstr = NSString(string="123") // 类型为NSString ``` @@ -385,6 +403,7 @@ let nstr = NSString(string="123") // 类型为NSString 可按照下面的方法在 string 和 NSString 之间转换: - string 转 NSString + ```ts let str = "abcd" // 类型为string // 方式一: @@ -394,6 +413,7 @@ let nstr2 = str as NSString // 类型为NSString ``` - NSString 转 string + ```ts let nstr3 = NSString(string="123") // 类型为NSString // 方式一: @@ -409,7 +429,6 @@ let str5 = nstr3 as string // 类型为string * 编译至 Kotlin 平台时,最大长度受系统内存的限制,超出限制会报错:`java.lang.OutOfMemoryError: char[] of length xxx would overflow`。 * 编译至 Swift 平台时,最大长度也受系统内存的限制,超出限制目前没有返回信息。 - #### Android 中的 Char 和 CharArray app-android平台存在一种 `kotlin.Char` 类型 [文档地址](https://kotlinlang.org/docs/characters.html) ,与UTS中长度为1的字符串比较类似。 @@ -442,7 +461,6 @@ arrayMock.add("l".toCharArray()[0]) arrayMock.add("o".toCharArray()[0]) console.log(arrayMock.joinToString("")); - ``` ## any类型 @any @@ -658,6 +676,7 @@ let javaDate = new JavaDate(utsDate.getTime().toLong()) ``` + java.util.Date 转 Date + ```uts import JavaDate from 'java.util.Date' ; @@ -665,7 +684,63 @@ let javaDate = new JavaDate(1709208329000) let utsDate = new Date(javaDate.getTime()) ``` +## 字节数组(ArrayBuffer)@arrayBuffer + +ArrayBuffer对象用来表示通用的原始二进制数据缓冲区。它是一个字节数组,通常在其他语言中称为byte array。你不能直接操作ArrayBuffer中的内容;而是要通过类型化数组对象对象来操作,它们会将缓冲区中的数据表示为特定的格式,并通过这些格式来读写缓冲区的内容。 + +### 定义字节数组 + +```ts +//直接使用ArrayBuffer初始化类型化数组 +const buffer = new ArrayBuffer(8); +const view = new Int32Array(buffer); +// 使用of方法 +var array = Int32Array.of(1, 2, 3) + +//使用from方法 +var array = Int32Array.from([1, 2, 3], (v : number, _ : number) : number => v + v); + +//ArrayBuffer.fromByteBuffer() 静态方法用于将android 原生的ByteBuffer对象转换为ArrayBuffer + var byteBuffer = ByteBuffer.allocate(100) + byteBuffer.put(1) + byteBuffer.put(2) + var buffer = ArrayBuffer.fromByteBuffer(byteBuffer) + console.log('arraybuffer_toByteBuffer', buffer) + +//ArrayBuffer 实例的 toByteBuffer() 方法返回一个android原生ByteBuffer对象。 + byteBuffer = buffer.toByteBuffer() + console.log('arraybuffer_toByteBuffer', byteBuffer) + byteBuffer.rewind() + console.log(byteBuffer[0])//1 + console.log(byteBuffer[1])//2 +``` + +### 类型化数组对象 + +[Float32Array](https://doc.dcloud.net.cn/uni-app-x/uts/buildin-object-api/float32array.html) + +[Float64Array](https://doc.dcloud.net.cn/uni-app-x/uts/buildin-object-api/float64array.html) + +[Int8Array](https://doc.dcloud.net.cn/uni-app-x/uts/buildin-object-api/int8array.html) + +[Int16Array](https://doc.dcloud.net.cn/uni-app-x/uts/buildin-object-api/int16array.html) + +[Int32Array](https://doc.dcloud.net.cn/uni-app-x/uts/buildin-object-api/int32array.html) + +[Uint8Array](https://doc.dcloud.net.cn/uni-app-x/uts/buildin-object-api/uint8array.html) + +[Uint8ClampedArray](https://doc.dcloud.net.cn/uni-app-x/uts/buildin-object-api/uint8clampedarray.html) + +[Uint16Array](https://doc.dcloud.net.cn/uni-app-x/uts/buildin-object-api/uint16array.html) + +[Uint32Array](https://doc.dcloud.net.cn/uni-app-x/uts/buildin-object-api/uint32array.html) + +[DataView](https://doc.dcloud.net.cn/uni-app-x/uts/buildin-object-api/dataview.html) + +### 更多API + +ArrayBuffer作为内置对象,还有更多API,[详见](buildin-object-api/arraybuffer.md) ## 数组(Array)@array @@ -680,6 +755,7 @@ Array,即数组,支持在单个变量名下存储多个元素,并具有执 如果开发者需要使用原始的kotlin的不可变长的Array,需使用 `kotlin.Array`。 需要使用平台专有数组类型的场景,也是如下2种情况: + 1. 某些系统API或三方原生SDK的入参或返回值强制指定了kotlin的原生数组类型。 2. uts新增的可动态变长的Array,在性能上不如固定length、不可变长的原始kotlin.Array。但也只有在巨大量的运算中才能体现出毫秒级的差异。 @@ -707,11 +783,13 @@ let a2:Array = []; //定义一个数字类型的空数组 ``` 3. 使用[]定义数组项的类型 + ```ts const a1: string[] = ['a', 'b', 'c']; //表示数组内容都是string ``` 4. 创建数组对象 + ```ts let a1 = new Array(1,2,3);//支持 let a2 = new Array(1,'2',3);//安卓平台支持, iOS 平台不支持,在 iOS 中创建 Any[] 数组请直接使用数组字面量,如 let a2 = [1. '2', 3] @@ -720,6 +798,7 @@ let a4 = Array(1,'2','3');//安卓平台支持, iOS 平台不支持,在 iOS ``` 5. uvue的data定义数组 + ```ts export default { data() { @@ -743,10 +822,8 @@ console.log(a1 instanceof Array) // 返回true - 注意:uts 不支持以 Array(arrayLength) 指定数组长度的方式创建一个数组。 ```ts - // 下面的写法中,a1 将是一个当前 length 为1, 元素是 100 的整型数组。而不是 length 为 100 ,由 100 个空槽组成的数组。 const a1: Array(100); // [100], 当前数组长度为1 - ``` ### 遍历数组对象 @@ -767,15 +844,15 @@ array1.forEach((element:string, index:number) => { #### kotlin专有数组类型 - 专有数组类型清单 - * kotlin.collections.List - * kotlin.Array - * kotlin.IntArray - * kotlin.FloatArray - * kotlin.ByteArray - * kotlin.LongArray - * kotlin.CharArray - * ... - + + * kotlin.collections.List + * kotlin.Array + * kotlin.IntArray + * kotlin.FloatArray + * kotlin.ByteArray + * kotlin.LongArray + * kotlin.CharArray + * ... - 专有数组类型定义方式 ```ts @@ -790,8 +867,7 @@ let kotlinArray = arrayOf("hello","world") 统一使用 `Array.fromNative` 将专有数据类型转换为 Array,下面列出了常见的场景: ```ts - - // kotlin.collections.List 转换 Array +// kotlin.collections.List 转换 Array let kotlinList = mutableListOf("hello","world") let utsArr1 = Array.fromNative(kotlinList) @@ -818,12 +894,8 @@ let kotlinArray = arrayOf("hello","world") // kotlin.CharArray 即 java 中的 char[] let b5 = charArrayOf(Char(66),Char(66),Char(81)) let c5 = Array.fromNative(b5) - - ``` - - 举个例子。如下代码向系统查询了有多少应用可以响应 `launcher`行为 ,返回的 resolveInfo 是一个 `List`。 ```ts @@ -872,18 +944,14 @@ let b = a.toKotlinList().map(function(it):Float{ let c = b.toTypedArray() // d 是 FloatArray let d = b.toFloatArray() - - ``` - + 特别说明: `Byte` 类型在 `kotlin` 中使用场景较为广泛,除表示数字,还常见于 `kotlin.ByteArray` 形式表示 文件,网络数据 等字节流。 下面列出了 `kotlin.ByteArray`的常用转换代码: - ```uts import Charsets from 'kotlin.text.Charsets' // 将ByteArray 以 ascii 编码转换为字符串 @@ -892,77 +960,67 @@ let str = byteArrayOf(65,66,67).toString(Charsets.ISO_8859_1) const str: string = 'hello world!' // 字符串以UTF-8编码转换为 ByteArray const bytes: ByteArray = str.toByteArray(Charsets.UTF_8) - ``` 更多`kotlin.ByteArray`的用法参考[文档](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-byte-array/) - - - - #### iOS 平台专有数组类型 ->UTS 中的 Array 对应到 Swift 中就是 Array, 方法是通用的,无需转换。一般情况下,使用 Array 即可。 -> ->但是,某些系统或者三方库 API 可能会要求 OC 的 NSArray、NSMutableArray 类型的数组,这个时候就需要进行转换。 +> UTS 中的 Array 对应到 Swift 中就是 Array, 方法是通用的,无需转换。一般情况下,使用 Array 即可。 +> +> 但是,某些系统或者三方库 API 可能会要求 OC 的 NSArray、NSMutableArray 类型的数组,这个时候就需要进行转换。 - 专有数组类型清单 - * NSArray - * NSMutableArray - + + * NSArray + * NSMutableArray - 专有数组类型定义方式 1. 创建 NSArray - - > NSArray 是 OC 中的不可变数组,顾名思义,数组创建完成之后就不可以再添加或者删除元素。因此,创建 NSArray 对象时就应该完成数组的初始化。可以通过以下方式创建 NSArray: - - ```ts - // 方式一: 创建一个空数组,注意数组创建后就不可改变,不能再添加或者删除元素,应避免使用该方式。 - let a: NSArray = NSArray() - - // 方式二: 用一个数组创建一个 NSArray, 推荐使用。同样,创建完成后数组不可变。 - let b: NSArray = NSArray(array=[1, 2, 3, 4]) // 等价于 any[],注意:不是等价于 number[] - - // 方式三: 用一个元素定义 NSArray, 不推荐使用 - let c: NSArray = NSArray(object=1) - - // 方式四:用不定长元素定义 NSArray, 可以使用 - let d: NSArray = NSArray(objects=1, "2", false, "ok") - - ``` - + + > NSArray 是 OC 中的不可变数组,顾名思义,数组创建完成之后就不可以再添加或者删除元素。因此,创建 NSArray 对象时就应该完成数组的初始化。可以通过以下方式创建 NSArray: + + ```ts + // 方式一: 创建一个空数组,注意数组创建后就不可改变,不能再添加或者删除元素,应避免使用该方式。 + let a: NSArray = NSArray() + + // 方式二: 用一个数组创建一个 NSArray, 推荐使用。同样,创建完成后数组不可变。 + let b: NSArray = NSArray(array=[1, 2, 3, 4]) // 等价于 any[],注意:不是等价于 number[] + + // 方式三: 用一个元素定义 NSArray, 不推荐使用 + let c: NSArray = NSArray(object=1) + + // 方式四:用不定长元素定义 NSArray, 可以使用 + let d: NSArray = NSArray(objects=1, "2", false, "ok") + ``` 2. 创建 NSMutableArray - + - NSMutableArray 是 OC 中的可变数组,其是 NSArray 的子类,可变数组创建后可以增加或者删除元素。NSArray 的所有创建方式也都适用于 NSMutableArray - - ```ts - // 方式一: 创建一个空数组,其类型等价于 any[] - let a: NSMutableArray = NSMutableArray() - a.add(1) //添加一个元素 - a.add("22") //添加一个元素 - a.add(false) //添加一个元素 - a.remove(1) //移除一个元素 - a.removeObject(at=2) //移除一个指定下标的元素 - a.removeAllObjects() //移除全部元素 - a.removeLastObject() //移除最后一个元素 - - // 方式二: 用一个数组创建一个 NSMutableArray, 推荐使用。 - let b: NSMutableArray = NSMutableArray(array=[1, 2, 3, 4]) // 等价于 any[],注意:不是等价于 number[] - - // 方式三: 用一个元素定义 NSMutableArray - let c: NSMutableArray = NSMutableArray(object=1) - - // 方式四:用不定长元素定义 NSMutableArray - let d: NSMutableArray = NSMutableArray(objects=1, "2", false, "ok") - - ``` - + + ```ts + // 方式一: 创建一个空数组,其类型等价于 any[] + let a: NSMutableArray = NSMutableArray() + a.add(1) //添加一个元素 + a.add("22") //添加一个元素 + a.add(false) //添加一个元素 + a.remove(1) //移除一个元素 + a.removeObject(at=2) //移除一个指定下标的元素 + a.removeAllObjects() //移除全部元素 + a.removeLastObject() //移除最后一个元素 + + // 方式二: 用一个数组创建一个 NSMutableArray, 推荐使用。 + let b: NSMutableArray = NSMutableArray(array=[1, 2, 3, 4]) // 等价于 any[],注意:不是等价于 number[] + + // 方式三: 用一个元素定义 NSMutableArray + let c: NSMutableArray = NSMutableArray(object=1) + + // 方式四:用不定长元素定义 NSMutableArray + let d: NSMutableArray = NSMutableArray(objects=1, "2", false, "ok") + ``` - 专有数组类型 转 Array ```ts - // 将 NSArray 转成 Array let a: NSArray = NSArray(array=[1, 2, 3, 4]) // 等价于 any[],注意:不是等价于 number[] let a1 = Array(a) @@ -970,7 +1028,6 @@ let a1 = Array(a) // 将 NSMutableArray 转成 Array let b: NSMutableArray = NSMutableArray(array=[1, 2, 3, 4]) // 等价于 any[],注意:不是等价于 number[] let b1 = Array(b) - ``` - Array 转 专有数组类型 @@ -988,15 +1045,14 @@ let a2 = a as NSArray // Array 转换成 NSMutableArray let a3: NSMutableArray = NSMutableArray(array= a) - ``` **注意:** - + 无论是 NSArray 还是 NSMutableArray 对象创建后都等价于 any[] 类型的数组,此时 Swift 不再有类型推导,可以往可变数组中添加任意类型的非空元素。 - + NSArray 和 NSMutableArray 类型的数组不接受空值 null, 任何情况下不要往这两种类型中注入 null。 否则,在运行时可能会引起应用闪退。 - + Array 类型不能通过 as 方式转换成 NSMutableArray 类型。 但是可以通过 as 方式 转换成 NSArray 类型。 - + Swift 中的 Array 是值类型,其和 TS 中 Array 的一点区别是 可以通 == 判断两个数组是否相等,只要两个数组的类型和元素一样,判等的结果就是 true。 ++ 无论是 NSArray 还是 NSMutableArray 对象创建后都等价于 any[] 类型的数组,此时 Swift 不再有类型推导,可以往可变数组中添加任意类型的非空元素。 ++ NSArray 和 NSMutableArray 类型的数组不接受空值 null, 任何情况下不要往这两种类型中注入 null。 否则,在运行时可能会引起应用闪退。 ++ Array 类型不能通过 as 方式转换成 NSMutableArray 类型。 但是可以通过 as 方式 转换成 NSArray 类型。 ++ Swift 中的 Array 是值类型,其和 TS 中 Array 的一点区别是 可以通 == 判断两个数组是否相等,只要两个数组的类型和元素一样,判等的结果就是 true。 ### 更多API @@ -1031,7 +1087,6 @@ const myMap = new Map([ [2, "two"], [3, "three"], ]); - ``` 注意在HBuilderX中console.log一个Map时,返回内容格式如下: @@ -1043,6 +1098,7 @@ const myMap = new Map([ 开头的[Object]代表其typeof的类型,Map代表它的实际类型,(3)是map的size,{...} 是Map的内容。 还可以把一个UTSJSONObject转为Map + ```ts let userA = { name: "zhangsan", @@ -1053,6 +1109,7 @@ let userMap = userA.toMap() //UTSJSONObject有toMap方法 ``` ### 验证类型 + ```ts console.log(typeof map1); //返回 object console.log(map1 instanceof Map); //返回 true @@ -1086,6 +1143,7 @@ UTSJSONObject,是一个类型,可以在变量的冒号后面使用,本节 首先,我们需要区分JSON对象,和由JSON对象组成的数组。 这是一个 UTSJSONObject 对象。jo 对象有2个属性,x和y,都是数字类型(类型没有声明是因为根据字面量自动推导了) + ```ts let jo: UTSJSONObject = { "x": 1, @@ -1094,6 +1152,7 @@ let jo: UTSJSONObject = { ``` 这是一个 UTSJSONObject 数组。其数组项里有2个 UTSJSONObject 对象,每个对象都有x和y这2个属性。注意`=`左边有`[]`来表示这是一个数组类型。 + ```ts let jr: UTSJSONObject[] = [ {"x": 1,"y": 2}, @@ -1104,6 +1163,7 @@ let jr: UTSJSONObject[] = [ 在 js 中,可以定义一个变量,随意接受对象字面量或数组字面量。但在 UTS 里不行。如果数据内容是数组字面量,就不能定义为 UTSJSONObject。 也就是下面的代码是错误的,不能把数组赋值给对象。在接收网络传输的json数据时,非常需要注意这个类型匹配问题。类型不匹配会造成代码错误和应用崩溃。 + ```ts let jo: UTSJSONObject = [{ "x": 1, @@ -1140,6 +1200,7 @@ let jo2 = { ``` 关于属性名是否需要使用引号包围的规则: + 1. 如果是对象字面量赋值,普通属性名称无需使用引号包围,但使用也没问题 2. 如果是对象字面量赋值,且属性名包括特殊符号,如:`-`,则必须两侧加引号包围。 3. 如果是JSON.parse()方法入参需要的字符串,则属性名必须使用双引号包围(web端规则也是如此) @@ -1178,6 +1239,7 @@ let jo = JSON.parse(s) // 这个代码适用于HBuilderX 3.9以前 所以从 HBuilderX 3.9起,`JSON.parse()`返回的类型改为`any`,即可能返回对象、也可能返回数组。这样就需要开发者自行再`as`一下来指定具体类型了。 新的写法是这样: + ```ts let s = `{"result":true, "count":42}` // 常见场景中,这个字符串更多来自于网络或其他应用传输。 let jo = JSON.parse(s) as UTSJSONObject @@ -1187,6 +1249,7 @@ let jr = JSON.parse(s) as UTSJSONObject[] ``` 当然,还有更简短的写法,使用HBuilderX 3.9新增的`JSON`的parseObject()和parseArray()方法: + ```ts let s = `{"result":true, "count":42}` // 常见场景中,这个字符串更多来自于网络或其他应用传输。 let jo = JSON.parseObject(s) @@ -1210,7 +1273,6 @@ js平台同时存在object和UTSJSONObject,且UTSJSONObject继承自object。 例如: ```ts - type Person = { age: number } @@ -1259,11 +1321,13 @@ merge( ``` 在js平台,UTSJSONObject和object在日常使用时差别不大,但有如下几点差别: + 1. UTSJSONObject多了一批通过keypath操作数据的方法 [见下](#keypath) 2. UTSJSONObject继承自object,原型链位置不同 3. instanceof返回值不同 ### 验证类型 + ```ts let jo = { x: 1, @@ -1289,19 +1353,25 @@ let rect = { 以上述 rect 为例,访问 UTSJSONObject 中的数据,有如下3种方式: #### 1. `.` 操作符 - 即 `rect.x`、`rect.size.width`。 - 这种写法比较简单,和js习惯一致,但在 UTS 存在以下限制: - - 仅限于web,在iOS/Android不支持`.`操作符。 +``` +即 `rect.x`、`rect.size.width`。 + +这种写法比较简单,和js习惯一致,但在 UTS 存在以下限制: +- 仅限于web,在iOS/Android不支持`.`操作符。 +``` #### 2. `[""]` 下标 - 即 `rect["x"]`。 - 这是一种通用的方式,不管通过字面量定义的 UTSJSONObject,还是通过 `JSON.parse()`,不管是 web、Android、iOS 哪个平台,都可以使用下标方式访问 UTSJSONObject 属性。 +``` +即 `rect["x"]`。 + +这是一种通用的方式,不管通过字面量定义的 UTSJSONObject,还是通过 `JSON.parse()`,不管是 web、Android、iOS 哪个平台,都可以使用下标方式访问 UTSJSONObject 属性。 - 但下标返回的数据,类型是any,想继续使用需要`as`为具体类型。 +但下标返回的数据,类型是any,想继续使用需要`as`为具体类型。 - 尤其是有子对象时,需要 `as` 后才能继续访问下一层数据。 +尤其是有子对象时,需要 `as` 后才能继续访问下一层数据。 +``` ```ts let rect = { @@ -1330,7 +1400,6 @@ console.log((rect["size"] as UTSJSONObject)["width"]) //80 使用as后需要整 console.log(rect.border[0].color); //报错,一旦使用了下标访问数组,后面就无法使用.操作符了 console.log(rect.border[0]["color"]); // red 但iOS无法使用.操作符 console.log((rect["border"] as UTSJSONObject[])[0]["color"]); // red - ``` 如果是 `JSON.parse` 解析的数据,只能通过下标访问,无法使用`.`操作符。因为`.`操作符的成立建立在编译器可确定类型的前提,字面量直接赋值可识别类型,`JSON.parse`无法识别类型。 @@ -1343,6 +1412,7 @@ console.log(listArr[0]["title"]); //第一组 ``` 多层级下标访问时需要使用 as 转换为 UTSJSONObject 或 `UTSJSONObject[]` + ```ts var j = { "subobj":{ @@ -1361,6 +1431,7 @@ keypath是把`.`操作符作为一个字符串传入了UTSJSONObject的一个方 相对于受限制`.`和需要经常as的下标,更推荐使用keyPath方式来操作UTSJSONObject。 以下面的 UTSJSONObject 为例 + ```ts let utsObj = { "username": "zhangsan", @@ -1388,6 +1459,7 @@ console.log(utsObj.getString("一个不存在属性")) // 打印结果:null ``` 需要特别注意的是:属性名 和 属性类型,都要正确,否则不会返回对应的属性结果 + ```ts console.log(utsObj.getNumber("age")) // 打印结果:12 console.log(utsObj.getNumber("agee")) // 名字不对,打印结果:null @@ -1424,7 +1496,6 @@ console.log(obj.getBoolean('data[2][0].b')) // 打印结果:true console.log(obj.getJSON('data[2][1]')) // 打印结果:{"b":"test"} console.log(obj.getArray('data[3]')) // 打印结果:[1, 2, 3] console.log(obj.getAny('data[1].a')) // 打印结果:2 - ``` 在所有的getXXX函数中 `getAny` 是一个特殊的存在,它可以获取属性,而不要求限制类型,他的返回值是 any 类型。 @@ -1439,6 +1510,7 @@ console.log(utsObj.getAny("address") as UTSJSONObject) ``` 除了直接使用UTSJSONObject外,在 uts 中使用json数据还有2种方式: + 1. UTSJSONObject.toMap() 转为Map对象 [见上](#map) 2. 把json字符串或对象字面量通过type转为自定义类型,这是ts里经常使用的方式 [见下](#type) @@ -1450,7 +1522,6 @@ console.log(utsObj.getAny("address") as UTSJSONObject) UTSJSONObject对象还有很多API,[详见](buildin-object-api/utsjsonobject.md) - ## type自定义类型@type `type`是关键字,用于给一个类型起别名,方便在其他地方使用。 @@ -1519,6 +1590,7 @@ console.log(personList[0].name); //返回zhangsan ### null的处理 但是需要注意,json数据可能不规范,有些属性缺失,此时就需要在定义type时设可为空: + ```ts type PersonType = { id : number, @@ -1583,6 +1655,7 @@ type PersonType = { 这里还要注意代码的执行顺序,执行 `address: PersonAddress` 时,这个类型必须已经被定义过。所以要被引用的类型必须定义在前面,后面才能使用这个类型。 那么嵌套的完整写法例子: + ```ts type PersonAddressType = { city: string, @@ -1607,6 +1680,7 @@ console.log(person.address.city) //beijing ``` 注意,在HBuilderX 3.9以前,有子对象的对象字面量或UTSJSONObject,无法直接被 as 为有嵌套的type,也需要对子对象进行 as 。 + ```ts let person = { id: 1, @@ -1638,6 +1712,7 @@ console.log(person?.name); // 返回zhangsan。由于person可能为null,pars ``` 注意上述代码中,如果`let person`时,想使用冒号定义类型,需要考虑parse失败的情况,要这么写: + ```ts type PersonType = { id: number, @@ -1648,6 +1723,7 @@ console.log(person?.name); // 返回zhangsan ``` 或者如果你确保jsonString的值一定是合法的、parse一定可以成功,那么也可以在定义的末尾!号断言,告诉编译器肯定没有问题,那么此后就可以不使用`?.`了 + ```ts type PersonType = { id: number, @@ -1660,6 +1736,7 @@ console.log(person.name); // 返回zhangsan 使用!断言,是强制编译器信任开发者的写法,编译器放过后,在运行期一旦person为null,调用`person.name`就会崩溃。而使用`person?.name`则不会崩溃,只会返回null。 #### 敏感字和符号@json_field + 在定义Type时键名必须符合变量命名规则(如第一个字符不能数字,不能包含空格或运算符,不能使用语言保留的关键字等), 如果json字符串中的键名不符合变量命名规则,比如有个key的名字叫"a+b",这种json转type会失败。 @@ -1676,17 +1753,20 @@ type ExampleType = { a_b: string } ``` + 以上示例定义的 ExampleType 类型,在 `a_b: string` 声明时添加注释 `@JSON_FIELD "a+b"`,表示: + - JSON.parse 时会将json字符中的键名"a+b"转换为ExampleType类型的"a_b"属性; - JSON.stringify 时会将ExampleType类型的"a_b"属性转换为json字符串中的"a+b"键名。 推荐的转换规则如下: + - 将不合法的字符(如空格、运算符等)转换为下划线“_”,如“a+b”转换为“a_b” - 将保留[关键词](keywords.md)(如class、enum等)转换时,在前面添加下划线,如“class”转换为“_class” - 如果转换后的名称已存在,在后面添加下划线`_`避免冲突,如同时存在“a+b”和“a-b”,分别转换为`a_b`和`a_b_` - 以下举例json字符串 `{"a+b":"addition value","a-b":"subtraction value","class":"classification value"}`,应该如何定义 type 才能使用 JSON.parse 转换 + ```ts type SpecialType = { /** @@ -1718,7 +1798,7 @@ let t:SpecialType = { console.log(JSON.stringify(t)) //输出: {"a+b":"value 1","a-b":"value 2","class":"value 3"} ``` ->以上`@JSON_FIELD`注释规则需要HBuilderX3.9.0+版本支持 +> 以上`@JSON_FIELD`注释规则需要HBuilderX3.9.0+版本支持 #### json转type工具 @@ -1737,6 +1817,7 @@ HBuilderX 3.9起内置了一个json转type工具,在`json编辑器`中选择 ### 为vue的data中的json定义类型 uvue文件中data中的json数据也涉及类型定义。此时注意:type定义必须放在`export default {}`前面。 + ```html