提交 ae099ee8 编写于 作者: WOSHIMAHAIFENG's avatar WOSHIMAHAIFENG

Update data-type.md

上级 8c777f13
```
```
# 类型@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<number> = []; //定义一个数字类型的空数组
```
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<ResolveInfo>`
```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<number,string>([
[2, "two"],
[3, "three"],
]);
```
注意在HBuilderX中console.log一个Map时,返回内容格式如下:
......@@ -1043,6 +1098,7 @@ const myMap = new Map<number,string>([
开头的[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
<script>
type PersonType = {
......@@ -1763,8 +1844,8 @@ uvue文件中data中的json数据也涉及类型定义。此时注意:type定
由于篇幅较长,示例另见:[request教程](../tutorial/request.md)
### type 类型的遍历
> HBuilderX3.9+
uts 为自定义 type 类型提供了迭代器,可以使用 for-in 遍历出 type 类型中的所有属性名
......@@ -1775,10 +1856,10 @@ let person : PersonType = { id: 1, name: "zhangsan", age: 18 }
for (let key in person) {
console.log(key) // 输出 "id", "name", "age"
}
```
### type 类型的下标访问
> HBuilderX3.9+
uts 为自定义 type 类型提供了下标操作,在适当的时机,可以使用下标的方式来读取或者修改 type 类型的属性值。
......@@ -1792,10 +1873,8 @@ console.log(person["id"]) //1
obj["age"] = 25
console.log(obj["age"]) //25
console.log(obj.age) //25
```
## undefined
js中的 undefined类型表示变量被定义,但是未赋值或初始化。
......@@ -1813,11 +1892,13 @@ uts有`运行时类型`和`开发时类型`的概念区别。
目前支持的`开发时类型`有:
### 相同运行时类型的字面量联合类型@literal-union-type
字面量联合类型,指相同类型的数字或字符串字面量,把多个字面量值以或的方式赋值给一个类型。
它常常用于在开发阶段的值域约束。
比如以下例子里,a1的值域只能是302或404或500,而b1的值域只能是"get"或"post"。
```ts
// 注意不要写在uvue页面的export default里面
type a = 302 | 404 | 500
......@@ -1834,6 +1915,7 @@ b1=="get1" // 当为b1赋值不在值域范围的新值时,ide会报红
相同字面量联合类型,在方法的参数值域定义里很常见。
但有几个注意:
1. 这些字面量必须是一个类型,或者统一是数字,或者统一是字符串。
2. 字面量联合类型只是`开发时类型`,在运行时,类型会变为number或string。
......@@ -1843,8 +1925,8 @@ b1=="get1" // 当为b1赋值不在值域范围的新值时,ide会报红
上一节提到的字符串字面量联合类型,其实是一种枚举型的值域范围约束。
但很多值域无法通过枚举表达。\
比如string.ColorString,代表一个合法的颜色字符串,"red"、"#000",这些是它的合法值域。\
但很多值域无法通过枚举表达。
比如string.ColorString,代表一个合法的颜色字符串,"red"、"#000",这些是它的合法值域。
再比如string.IDString代表页面上合法的组件的id属性值清单,string.ImageURIString则代表工程下合法的图片路径清单。
HBuilder支持给变量定义特殊值域string类型,这些类型在HBuilder里都可以得到更好的代码提示和语法校验。
......@@ -1856,6 +1938,7 @@ HBuilder支持给变量定义特殊值域string类型,这些类型在HBuilder
## 联合类型@union-type
由于kotlin和swift限制,uts在App平台支持的联合类型有限:
- 支持 [|null](#null) (即可为空)
- [字面量联合类型](#literal-union-type)
......@@ -1876,3 +1959,11 @@ uts内置的类型,包括浏览器、Android、iOS内置的类型,在编译
**注意**
- 编译到js时联合类型等复杂类型在编译后会被擦除
```
```
```
```
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册