写点什么

【HarmonyOS NEXT】ArkTs 数据类型解析与使用

作者:冉冉同学
  • 2024-12-16
    山东
  • 本文字数:11147 字

    阅读完需:约 37 分钟

【HarmonyOS NEXT】ArkTs数据类型解析与使用

1. 背景

  1. 为什么设计 ArkTS?

  2. 1.1 其它语言有版权【Java?Kotlin?】以及历史问题【Java 内存?】

  3. 1.2 生态,可复用前端生态的三方库,兼容 JS/TS 语言生态

  4. ArkTs 解决了 JS/TS 中的哪些问题?

  5. 2.1 程序健壮性: JS 是动态类型【运行期间才去做数据类型检查,且类型可以随便改变】,不利于程序的健壮性。

  6. 2.2 性能问题: TS 虽然是静态类型,但是它的类型检查可配置可关闭,而且编译后类型信息丢失,会增加运行的时编译和执行字节码耗时。ArkCompiler利用 ArkTS 的静态类型信息,进行类型推导并生成对象描述和内联缓存,**加速运行时对字节码的解释执行;**AOT(Ahead-of-Time)Compiler 利用静态类型信息直接将字节码编译生成优化机器码,让应用启动即可运行高性能代码,提升应用启动和运行性能。

  7. 2.3 JS 运行流程: 解析源码>编译字节码>执行字节码>获取 Profile 信息>编译优化机器码>执行优化机器码

  8. 2.4 ArkTS 编译流程: 解析源码>编译字节码>获取 Profile 信息>编译优化机器码>打包字节码和优化字节码

  9. 2.5 ArkTS 运行流程: 执行优化机器码>执行字节码

  10. 2.6 安全: ArkCompiler 会把 ArkTS/TS/JS 编译为方舟字节码,运行时直接运行方舟字节码。并且 ArkCompiler 使用多种混淆技术提供更高强度的混淆与保护,使得 HarmonyOS 应用包中装载的是多重混淆后的字节码,有效提高了应用代码安全的强度。

  11. 2.7 并发: JS/TS 中并发的 API 不够简洁,而且支撑不了复杂业务的开发场景,而 ArkTS 中的 TaskPool 会绑定系统的调度优先级,并且支持负载均衡(自动扩缩容),可支撑各种业务场景

2. 前言

  无论是 Android 还是 iOS 开发,都提供了多种数据类型用于常见的业务开发,和 Java/Objective-C 一样 ArkTs 也有很多和 Java/Objective-C 中相同的数据类型,当然也有不同的。


  ArkTS 和 JS\TS 之间的关系



关于 ES 是什么,可以看我这边整理的一篇文章 Javascript[ECMAScript] ES6、ES7、ES8、ES9、ES10、ES11、ES12、ES13、ES14[2023]新特性


  ArkTS 是TypeScript的超集,其数据类型也是基于TypeScript而来,除了原始 5 种数据类型之外,还有一些 ECMAScript 中的新类型,以及包含常见的枚举、任意类型等等,大概有十多种,但常用的就那么几种。


数据类型汇总如下:



为了保证开发正确性和性能,ArkTS 中取消了 JS 中的 symbol 类型,以及 TS 中的unknown 和any类型

3. 类型声明

3.1 变量声明

  以关键字 let 开头的声明引入变量,该变量在程序执行期间可以具有不同的值。【注意,这里不能使用 JS/TS 中的 var,因为 let 关键字可以在块级作用域中声明变量,帮助程序员避免错误。因此,ArkTS 不支持 var,请使用 let 声明变量。】


这里的 let 类似于 kotlin 中的 var


let hi: string = 'hello';hi = 'hello, world';
复制代码

3.2 常量声明

  以关键字 const 开头的声明引入只读常量,该常量只能被赋值一次。对常量重新赋值会造成编译时错误。


这里的 const 类似于 kotlin 中的 val


const hello: string = 'hello';
复制代码

3.3 自动类型推断

  由于 ArkTS 是一种静态类型语言,所有数据的类型都必须在编译时确定。但是,如果一个变量或常量的声明包含了初始值,那么开发者就不需要显式指定其类型。ArkTS 规范中列举了所有允许自动推断类型的场景。


以下示例中,两条声明语句都是有效的,两个变量都是 string 类型


let hi1: string = 'hello';let hi2 = 'hello, world';//使用JS的同学注意了:这里和JS不一样,ArkTS中一旦类型初始化的时候就确定了,之后就无法更改。例如:// hi2 = 123// 错误flag = '你好世界'  //正确
复制代码

4. 基础数据类型

JavaScript 中存在两套类型系统分别为原生类型(Base types)和对象类型(Object types)【类似于 Java 中装箱和拆箱】,ArkTS 也继承了这一特性。对象类型和原生类型相比,会提供一些额外的方法,例如 string 和 String


let msg: string = 'Hello world!';let msg2: String = 'Hello world!';let msg22 = 'Hello world!';  //字面上没有定义类型let msg3: String = new String('Hello world!');
console.log(typeof(msg)); //stringconsole.log(typeof(msg2)); //stringconsole.log(typeof(msg22)); //stringconsole.log(typeof(msg3)); //objectconsole.log((msg === msg2)+""); //trueconsole.log((msg === msg3)+""); //falseconsole.log((msg2 === msg3)+""); //false
复制代码

4.1 Number 类型

  凡是表示数值的,不管是二进制还是八进制,还是其他进制,长整数,小数,负数等等,只有一个类型表示,那就是 number。局部声明须带关键字

4.1.1 原生 number

let a: number = 0 //整数let b: number = -100 //负数let c: number = 88.88 //小数let d: number = 0b1010 //二进制 ES6let e: number = 0o744 //八进制  ES6
复制代码

4.1.2 对象 Number

let a: Number = new Number(0) //整数let b: Number = new Number(-100) //负数let c: Number = new Number(88.88) //小数let d: Number = new Number(0b1010) //二进制 ES6let e: Number = new Number(0o744) //八进制  ES6
复制代码

4.1.3 上限

  在 ArkTS 中,Number 类型的上限由 JavaScript 的 Number 类型的上限决定,因为 ArkTS 的 Number 类型是基于 JavaScript 的 Number 类型。


  JavaScript 中的 Number 类型是基于 IEEE 754 标准的双精度浮点数表示,它的上限由标准规定为 1.7976931348623157e+308。这意味着 JavaScript 中的 Number 类型可以表示的最大值是约 1.8 x 10^308。


const maxNumber: number = Number.MAX_VALUE;console.log(maxNumber.toString()); // 输出 1.7976931348623157e+308
const infinity: number = Number.MAX_VALUE * 2;console.log(infinity.toString()); // 输出 Infinity
复制代码

【必看】注意注意:

  因为 ArkTS 的 Number 类型是基于 JavaScript 的 Number 类型。那么在 JS 中著名精度丢失问题,在 ArkTS 中也依旧存在,这里想起一张图。。。此处先只解决图里面精度丢失的问题,其它问题后续会出详细的文档来说明和解决。



**解决办法:**可以用成熟的三方库来解决:https://ohpm.openharmony.cn/#/cn/detail/bignumber.js


【鸿蒙版的 bigNumber】


**问题原因:**可看这个https://juejin.cn/post/7216917459009536060


// Demo 1 - 乘法异常console.log(18.9 * 100+"") // 1889.9999999999998console.log(64.68 * 100+"") // 6468.000000000001
// Demo 2// 典中典console.log((0.1 + 0.2 === 0.3)+"") // falseconsole.log((0.1 === 0.1)+"") // true
....等等还有其它的例子```typescript
## 4.2 Boolean类型boolean类型由true和false两个逻辑值组成。 通常在条件语句中使用boolean类型的变量:```typescriptlet isDone: boolean = false;// ...if (isDone) { console.log ('Done!');}
复制代码

4.3 String 类型

string 代表字符序列;可以使用转义字符来表示字符。


字符串字面量由单引号(')或双引号(")之间括起来的零个或多个字符组成。字符串字面量还有一特殊形式,是用反向单引号(`)括起来的模板字面量。


let s1 = 'Hello, world!\n';let s2 = 'this is a string';let a = 'Success';let s3 = `The result is ${a}`;//这里的${} 是ES6 中的模板字符串,和Kotlin中字符串的${}功能一样
复制代码

4.4 Object 类型

  Object 类型是所有引用类型的基类型。任何值,包括基本类型的值(它们会被自动装箱),都可以直接被赋给 Object 类型的变量。


在 ArkTs 中,不管你是一个普通的对象,还是一个数组,元组,集合等等,都是一个对象类型。


let str = new String()let obj: object = str
let test = new Object()let numberArray = [1, 2, 3, 4, 5]let numberArrayObj = new Array<number>()let tuple: [string, number] = ["age", 10]let map = new Map<string, number>()console.log("当前数据类型1:" + typeof test)//objectconsole.log("当前数据类型2:" + typeof numberArray)//objectconsole.log("当前数据类型3:" + typeof numberArrayObj)//objectconsole.log("当前数据类型4:" + typeof tuple)//objectconsole.log("当前数据类型5:" + typeof map)//object
复制代码

4.5 Array 类型

array,即数组,是由可赋值给数组声明中指定的元素类型的数据组成的对象。数组可由数组复合字面量(即用方括号括起来的零个或多个表达式的列表,其中每个表达式为数组中的一个元素)来赋值。数组的长度由数组中元素的个数来确定。数组中第一个元素的索引为 0。数组有两种声明方式,一种是使用 Array 对象,一种直接使用中括号[]。


let array = Array<number>()
let arr = []
//默认值let arr2 = [1, 2, 3, 4, 5]
let array2 = Array<number>(1, 2, 3, 4, 5)
//注意:数组如果没声明类型,它在JS/TS中可以并存多种类型数据的
let arr = [1, "字符串", true, new Object()]let array = Array<Object>(1, "字符串", true, new Object())
复制代码

4.6 Tuple 类型

Tuple Type(中文翻译:元组类型),可以认为是一个有顺序的数组类型。有以下特点:


  1. 可以明确知道包含了多少元素(这里的元素是类型)

  2. 可以明确知道每个类型所在的位置

  3. 长度固定,元组类型的变量需要为每一个位置定义对应类型的值


let tuple1: [string, number] = ["hello", 10]; // 正确let tuple2: [string, number] = [10, "hello"]; // 错误
复制代码

4.7 Enum 类型

enum 类型,又称枚举类型,是预先定义的一组命名值的值类型,其中命名值又称为枚举常量。使用枚举常量时必须以枚举类型名称为前缀。默认情况下,从 0 开始为元素编号。当然也可以更改默认的值。如下红绿蓝


enum ColorSet { Red, Green, Blue }console.log(typeof ColorSet.Red)//numberconsole.log(ColorSet.Red.toString())//0console.log(ColorSet.Green.toString())//1
复制代码


常量表达式可以用于显式设置枚举常量的值。


enum ColorSet { Red = '红色', Green = '绿色', Blue = '蓝色'}console.log(typeof ColorSet.Red)//stringconsole.log(ColorSet.Red.toString())//红色console.log(ColorSet.Green.toString())//绿色console.log(ColorSet.Blue.toString())//蓝色
复制代码

4.8 Union 类型

union 类型,即联合类型,是由多个类型组合成的引用类型。联合类型包含了变量可能的所有类型。


//输入值可是多种类型function getParamsType(params?: String | Number): string {  return typeof params}
console.log(getParamsType("姓名"))//stringconsole.log(getParamsType(18))//number
//返回值可是多种类型function getParamsType(params?: String | Number): string | boolean { if (params == "1") { return true } else { return "你好" }}
console.log(getParamsType()+"")//你好console.log(getParamsType(1)+"")//trueconsole.log(getParamsType(2)+"")//你好
复制代码

4.9 Undefined 类型

undefined 类型只包含一个值 undefined,表示未定义(即还未给出定义,以后可能会有定义)。


class Person {  name?: String  constructor(name?: String) {    this.name = name;  }}//未赋值的情况let p = new Person();console.log(p?.name?.toString())//输出undefined
//赋值的情况let p = new Person("葫芦娃");console.log(p?.name?.toString())//输出葫芦娃
复制代码

4.10 Null 类型

null 类型也只包含一个值 null,表示为空(即此处没有值)。


class Person {  name?: String | null = null}
let p = new Person();if (p.name === undefined) { console.log("这里是 undefined")} else if (p.name === null) { console.log("这里是 null")//会输出这一行} else { console.log("这里是 string")}
复制代码

4.11 Aliases 类型[类型别名]

Aliases 类型为匿名类型(数组、函数、对象字面量或联合类型)提供名称,或为已有类型提供替代名称。


type Matrix = number[][];//声明数组type Handler = (s: string, no: number) => string;//声明函数type Predicate <T> = (x: T) => Boolean;//声明函数type NullableObject = String | null;//声明联合类型
//使用class Person { name?: NullableObject = null}
复制代码

4.12 BigInt 类型

Number 类型表示的是双精度浮点数,在处理大数时可能会出现精度丢失的问题。为了解决这个问题,ES11(2020)推出了BitInt类型,它可以表示大于 2^53 - 1 的整数。这原本是 Javascript 中可以用 Number 表示的最大数字。BigInt 可以表示任意大的整数。


可以用在一个整数字面量后面加 n 的方式定义一个 BigInt ,如:10n,或者调用函数 BigInt()(但不包含 new 运算符)并传递一个整数值或字符串值。


const bigInt = BigInt(9007199254740991);console.log(typeof bigInt)//bigint
复制代码

4.13 Void 类型

void 类型用于指定函数没有返回值。


此类型只有一个值,同样是 void。由于 void 是引用类型,因此它可以用于泛型类型参数。


function test1():void{}
function test2() {}
console.log(typeof test1())//undefinedconsole.log(typeof test2())//undefined
复制代码


声明一个 void 类型的变量没有什么大用,因为在 ArkTS 你只能为它赋予 undefined


let unusable: void = undefined;
复制代码

4.14 Never 类型

never 类型表示的是那些永不存在的值的类型。 例如, never 类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型; 变量也可能是 never 类型,当它们被永不为真的类型保护所约束时。


// 返回never的函数必须存在无法达到的终点function error(message: string): never {    throw new Error(message);}
// 推断的返回值类型为neverfunction fail() { return error("Something failed");}
// 返回never的函数必须存在无法达到的终点function infiniteLoop(): never { while (true) { }}
复制代码

5. 运算符、语句

ArkTS 中的运算符和语句和主流的语言类似,这里不再赘述,详情见:运算符、语句

6. 集合类型

6.1 Array

用于存储有序的一组相同类型的元素。

6.1.1 基本 API

  • concat() 用于合并两个或多个数组。

  • slice() 返回一个新数组,包含原数组中指定范围内的元素。

  • splice() 用于添加或删除数组中的元素。

  • push() 将一个或多个元素添加到数组的末尾,并返回新的长度。

  • pop() 从数组中删除最后一个元素,并返回该元素的值。

  • shift() 从数组中删除第一个元素,并返回该元素的值。

  • unshift() 将一个或多个元素添加到数组的开头,并返回新的长度。

  • reverse() 颠倒数组中元素的顺序。

  • sort() 对数组元素进行排序。

  • map() 创建一个新数组,其结果是该数组中的每个元素都调用了提供的函数后的返回值。

  • filter() 创建一个新数组,其包含通过所提供函数实现的测试的所有元素。

  • reduce() 将数组元素计算为一个值(从左到右)。

  • forEach() 对数组的每个元素执行一次提供的函数。

  • indexOf() 返回在数组中可以找到给定元素的第一个索引,如果不存在则返回 -1。

  • lastIndexOf() 返回指定元素在数组中的最后一个索引,如果不存在则返回 -1。

  • includes() 判断数组是否包含指定的值。

  • join() 将数组中的所有元素连接成一个字符串。

  • entries() 返回一个数组迭代器对象,该对象包含数组的键值对。

  • find() 返回满足提供的测试函数的第一个元素的值。

  • findIndex() 返回满足提供的测试函数的第一个元素的索引。

  • .............常用的例子可以参考这篇文章:JavaScript Array 奇技淫巧

6.2 ArrayList

ArkTS 独有,参考 Java 实现的,使用前需要导包


import ArrayList from '@ohos.util.ArrayList';
复制代码


ArrayList 是一种线性数据结构,底层基于数组实现。ArrayList 会根据实际需要动态调整容量,每次扩容增加 50%。


ArrayList 和Vector相似,都是基于数组实现。它们都可以动态调整容量,但 Vector 每次扩容增加 1 倍。


ArrayList 和 LinkedList 相比,ArrayList 的随机访问效率更高。但由于 ArrayList 的增删操作会影响数组内其他元素的移动,LinkedList的增加和删除操作效率更高。


推荐使用场景: 当需要频繁读取集合中的元素时,推荐使用 ArrayList。

6.2.1 常见 API

和 Java 类似,具体见 https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V1/js-apis-arraylist-0000001630146253-V1


class C1 {  name: string = ""  age: string = ""}let arrayList: ArrayList<string | number | boolean | Array<number> | C1> = new ArrayList();let result1 = arrayList.add("a");let arrayList1: ArrayList<number> = new ArrayList();let result2 = arrayList.add(1);let b = [1, 2, 3];let result3 = arrayList.add(b);let c : C1 = {name: "Dylon", age: "13"}let result4 = arrayList.add(c);let result5 = arrayList.add(false);
复制代码

6.3 Map

ES6 之前一般用 Object 来实现键/值存储。Map 是 ES6 新增特性,是一种新的集合类型,为这门语言带来了真正的键/值存储机制。ArkTS 中也可使用该类型。

6.3.1 基本 API

  • 创建映射,new Map(),Map 构造函数也可接收一个可选的可迭代对象

  • size,使用 Map 实例的 size 属性获取映射中键值对的数量

  • set(),使用 Map 实例的 set() 方法可以向应设置再添加键/值对,支持链式调用,因为 set() 方法返回 Map 实例

  • has(),查询映射是否存在指定键

  • get(),从映射中获取指定键的值

  • delete(),从映射中删除指定的键/值对

  • clear(),清除映射中的所有键/值对


// 创建一个空的 Map 实例const m = new Map<string,string>()// 使用 set() 方法添加键/值对m.set('k3', 'v3').set('k4', 'v4')console.log(m.size.toString()) // 2console.log(m.has('k3')+"") // trueconsole.log(m.get('k3')+"") // v3//遍历m.forEach((value,key)=>{  console.log(`key:${key} value:${value}`)})// key:k3 value:v3// key:k4 value:v4// keysfor (let key of m.keys()) {  console.log(key)}// 'k3'// 'k4'
// valuesfor (let val of m.values()) { console.log(val)}// 'v3'// 'v4'
// delete、clearconsole.log(m.delete('k3')+"") //trueconsole.log(m.has('k3')+"") // falsem.clear()console.log(m.size.toString()) // 0
复制代码

6.4 Record

泛型 Record<K, V>用于将类型(键类型)的属性映射到另一个类型(值类型)。常用对象字面量来初始化该类型的值


使用场景:


  • 如果你需要表示一个具有固定结构的数据,例如一个学生的记录,其中包含姓名、年龄、成绩等字段,那么使用 Record 是合适的选择。

  • 如果你需要表示一个动态的、可变的数据集合,并且需要根据键来查找、插入或删除值,那么使用 Map 是更好的选择。例如,你可以使用 Map 来存储用户的偏好设置,其中键是设置的名称,值是设置的值。

6.4.1 常见 API

let map: Record<string, number> = {  'John': 25,  'Mary': 21,}map['John']; // 25
复制代码

6.5 HashMap

ArkTS 独有,参考 Java 实现的,使用前需要导包


import HashMap from '@ohos.util.HashMap'; 
复制代码


HashMap 底层使用数组+链表+红黑树的方式实现,查询、插入和删除的效率都很高。HashMap 存储内容基于 key-value 的键值对映射,不能有重复的 key,且一个 key 只能对应一个 value。


HashMap 和TreeMap相比,HashMap 依据键的 hashCode 存取数据,访问速度较快。而 TreeMap 是有序存取,效率较低。


HashSet基于 HashMap 实现。HashMap 的输入参数由 key、value 两个值组成。在 HashSet 中,只对 value 对象进行处理。


推荐使用场景: 需要快速存取、删除以及插入键值对数据时,推荐使用 HashMap。

6.5.1 常见 API

和 Java 类似,具体见 https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/nonlinear-container-V5#hashset


const hashMap: HashMap<string, number> = new HashMap();hashMap.set("squirrel", 123);hashMap.set("sparrow", 356);let newHashMap: HashMap<string, number> = new HashMap();newHashMap.set("newMap", 99);hashMap.setAll(newHashMap);
复制代码

6.6 WeakMap

WeakMap(弱映射)是 ECMAScript 6 新增的集合类型,它是一种增强的键值对存储机制。是 Map 的“兄弟”类型,其 API 也是 Map 的子集。WeakMap 中的 “weak(弱)” 描述的是 JavaScript 垃圾回收程序对待“弱映射”中的键的处理方式。

6.6.1 弱键

WeakMap 中的 "weak" 表示弱映射中的键是 “弱弱的拿着”。意思就是说,这些键不属于正式的引用,不会组织垃圾回收。当引用释放后对应的键值很快就会被垃圾回收。这点和 Map 不同,Map 中的对应项会编程无法直接访问的数据。

6.6.2 API

WeakMap 的 API 是 Map 的一个子集,除了以下内容之外 和 Map 没有任何区别:


  • WeakMap 的键必须是引用类型,如果提供非引用的类型的键会导致整个初始化失败

  • WeakMap 不可迭代,因为 WeakMap 中的键值对在任何时候都可能被销毁,所以没必要提供迭代能力。当然,clear() 也不存在。因为不可迭代,所以如果没有键的引用,则无法从弱映射中取的对应的值,即便代码可以访问 WeakMap 实例,也没办法取得其中的内容(Map 可以通过 迭代的方式查看)。

  • set(),使用 Map 实例的 set() 方法可以向应设置再添加键/值对,支持链式调用,因为 set() 方法返回 Map 实例

  • has(),查询映射是否存在指定键

  • get(),从映射中获取指定键的值

  • delete(),从映射中删除指定的键/值对


// 创建一个空的 Map 实例const m = new WeakMap<String,string>()// 使用 set() 方法添加键/值对m.set('k3', 'v3').set('k4', 'v4')console.log(m.has('k3')+"") // trueconsole.log(m.get('k3')+"") // v3// delete、clearconsole.log(m.delete('k3')+"") //trueconsole.log(m.has('k3')+"") // false
复制代码

6.7 Set

Set(集合)是 ECMAScript 6 新的一种集合类型,一种新的集合数据结构。用于存储唯一值的集合。


Set 会维护值插入时的顺序,所以支持按顺序迭代。可以通过集合实例上的迭代器来按顺序迭代集合中的内容。


**注意:**在 TypeScript 中的 Set 集合里面, keys() 和 values() 方法返回的实际上是同一个迭代器。这是因为 Set 是一个存储唯一值的数据结构,所以键和值是同一个值。所以遍历的时候建议使用 values()

6.7.1 基本 API

  • new Set(),创建集合,接收一个可选的可迭代对象作为构造函数的参数

  • ins.size,获取集合中元素的个数

  • ins.add(xx),向集合中添加新元素,返回集合实例(支持链式调用)

  • ins.has(xx),判断集合中是否存在指定元素

  • ins.delete(xx),从集合中删除指定元素,返回一个布尔值,表示集合中是否存在要删除的值

  • ins.clear(),清空集合

  • ins.keys() 方法返回一个新的迭代器对象,它包含 Set 对象中所有元素的值作为键。

  • ins.values() 方法也返回一个新的迭代器对象,它包含 Set 对象中所有元素的值。


const s = new Set(['v1', 'v2'])console.log(s.size.toString()) // 2s.add('v3')console.log(s.has('v3').toString()) // true
for (const v of s.keys()) { console.log(`keys:${v}`)}// keys:v1// keys:v2// keys:v3for (const v of s.values()) { console.log(`values:${v}`)}// values:v1// values:v2// values:v3s.forEach((val, key) => { console.log(`key:${key} val:${val}`)})// key:v1 val:v1// key:v2 val:v2// key:v3 val:v3
s.delete('v3')console.log(s.has('v3').toString()) // falses.clear()console.log(s.size.toString()) // 0
复制代码

6.8 HashSet

ArkTS 独有,参考 Java 实现的,使用前需要导包


import HashSet from '@ohos.util.HashSet';
复制代码


HashSet 基于HashMap实现。在 HashSet 中,只对 value 对象进行处理。


HashSet 和TreeSet相比,HashSet 中的数据无序存放,即存放元素的顺序和取出的顺序不一致,而 TreeSet 是有序存放。它们集合中的元素都不允许重复,但 HashSet 允许放入 null 值,TreeSet 不建议插入空值,可能会影响排序结果。


推荐使用场景: 可以利用 HashSet 不重复的特性,当需要不重复的集合或需要去重某个集合的时候使用。

6.8.1 基本 API

和 Java 类似,具体见 https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/nonlinear-container-V5#hashset

6.9 WeakSet

WeakSet(弱映射)是 ECMAScript 6 新增的集合类型,它是一种增强的键值对存储机制。是 Set 的“兄弟”类型,其 API 也是 Set 的子集。WeakMap 中的 “weak(弱)” 描述的是 JavaScript 垃圾回收程序对待“弱映射”中的键的处理方式。

6.9.1 基本 API

  • add(value):向集合中添加一个值。

  • delete(value):从集合中删除一个值。

  • has(value):检查集合中是否存在一个值。


// 创建一个 WeakSetconst weakSet = new WeakSet();
// 创建一个对象并将其添加到 WeakSet 中const object1 = new Object();weakSet.add(object1);
// 检查 WeakSet 中是否存在该对象console.log(weakSet.has(object1)+""); // 输出:true
// 删除该对象weakSet.delete(object1);
// 再次检查 WeakSet 中是否存在该对象console.log(weakSet.has(object1)+""); // 输出:false
复制代码

6.10 更多集合类型

还有一些不常用的集合类型,这里就不做过多说明,详情可点下方链接


7. 空安全

默认情况下,ArkTS 中的所有类型都是不可为空的,因此类型的值不能为空。这类似于 TypeScript 的严格空值检查模式(strictNullChecks),但规则更严格。在下面的示例中,所有行都会导致编译时错误:


let x: number = null;    // 编译时错误let y: string = null;    // 编译时错误let z: number[] = null;  // 编译时错误
复制代码


可以为空值的变量定义为联合类型(Union)T | null。


let x: number | null = null;x = 1;    // okx = null; // okif (x != null) { /* 做你想做的事 */ }
复制代码


例子 2


// 如果 obj 或 obj.foo 或 obj.foo.bar 为 null 或 undefined,value 将为 undefined,否则为 42


let obj = { foo: { bar: 42 } };let value = obj?.foo?.bar; 
复制代码

7.1.1 非空断言运算符 !

后缀运算符!可用于断言其操作数为非空。


应用于空值时,运算符将抛出错误。否则,值的类型将从 T | null 更改为 T:


class C {  value: number | null = 1;}
let c = new C();let y: number;y = c.value + 1; // 编译时错误:无法对可空值作做加法y = c.value! + 1; // ok,值为2
//未赋值的空值属性,值为undefinedlet nick: string | nullconsole.log(nick!)//undefined
复制代码

7.1.2 空值合并运算符 ??

空值合并二元运算符??用于检查左侧表达式的求值是否等于 null。如果是,则表达式的结果为右侧表达式;否则,结果为左侧表达式。


换句话说,a ?? b 等价于三元运算符 a != null ? a : b。


在以下示例中,getNick 方法如果设置了昵称,则返回昵称;否则,返回空字符串:


class Person {  // ...  nick: string | null = null  getNick(): string {    return this.nick ?? '';  }}
let x = null;let y = x ?? 5; // 如果 x 为 null 或 undefined,y 将为 5,否则为 x 的值
复制代码

7.1.3 空值合并赋值运算符 (??=)

用于将默认值赋给一个变量,当该变量的值为 null 或 undefined 时。如果一个变量的值为 null 或 undefined,可以使用 ??= 运算符将一个默认值赋给该变量。


**??= **运算符在日常开发中有多种使用场景:

7.1.3.1 默认参数赋值

在函数中可以使用 ??= 来为可能未传入的参数提供默认值:


function greet(name) {  name ??= 'Guest';  console.log(`Hello, ${name}!`);//hello Guest}
class AppConfig { theme: string = 'dark'; notifications?: string fontSize?: number}
let appConfig = new AppConfig()console.log(JSON.stringify(appConfig))//输出:{"theme":"dark"}
// 确保所有配置项都有默认值appConfig.theme ??= 'light'; // 已经有值,所以保持不变appConfig.notifications ??= "你好"; // 之前是 undefined,现在赋值为 你好appConfig.fontSize ??= 14; // 之前是 undefined,现在赋值为 14
console.log(JSON.stringify(appConfig)); // 输出:{ theme: 'light', notifications: 你好, fontSize: 14 }
复制代码


发布于: 刚刚阅读数: 4
用户头像

冉冉同学

关注

还未添加个人签名 2018-05-12 加入

还未添加个人简介

评论

发布
暂无评论
【HarmonyOS NEXT】ArkTs数据类型解析与使用_鸿蒙_冉冉同学_InfoQ写作社区