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

2024-12-17 19:31:47
127次阅读
0个评论

1. 背景

  1. 为什么设计ArkTS?
    1.1 其它语言有版权【Java?Kotlin?】以及历史问题【Java内存?】
    1.2 生态,可复用前端生态的三方库,兼容JS/TS语言生态
  2. ArkTs解决了JS/TS中的哪些问题?
    2.1 程序健壮性: JS是动态类型【运行期间才去做数据类型检查,且类型可以随便改变】,不利于程序的健壮性。
    2.2 性能问题: TS虽然是静态类型,但是它的类型检查可配置可关闭,而且编译后类型信息丢失,会增加运行的时编译和执行字节码耗时。ArkCompiler利用ArkTS的静态类型信息,进行类型推导并生成对象描述和内联缓存,**加速运行时对字节码的解释执行;**AOT(Ahead-of-Time)Compiler利用静态类型信息直接将字节码编译生成优化机器码,让应用启动即可运行高性能代码,提升应用启动和运行性能。
    2.3 JS运行流程: 解析源码>编译字节码>执行字节码>获取Profile信息>编译优化机器码>执行优化机器码
    2.4 ArkTS编译流程: 解析源码>编译字节码>获取Profile信息>编译优化机器码>打包字节码和优化字节码
    2.5 ArkTS运行流程: 执行优化机器码>执行字节码
    2.6 安全: ArkCompiler会把ArkTS/TS/JS编译为方舟字节码,运行时直接运行方舟字节码。并且ArkCompiler使用多种混淆技术提供更高强度的混淆与保护,使得HarmonyOS应用包中装载的是多重混淆后的字节码,有效提高了应用代码安全的强度。
    2.7 并发: JS/TS 中并发的API不够简洁,而且支撑不了复杂业务的开发场景,而ArkTS中的TaskPool会绑定系统的调度优先级,并且支持负载均衡(自动扩缩容),可支撑各种业务场景

2. 前言

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

4b8e8282-5308-4d5e-ae44-75cbbcf6b61e.png

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

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

数据类型汇总如下: | 数据类型 | 包装类 | 概述 | | --- | --- | --- | | number | Number | 数字类型 | | boolean | Boolean | 布尔类型 | | string | String | 字符串类型 | | object | Object | 对象 | | Array | Array | |数组类型 | tuple | | 元组类型 | | enum | | 枚举类型 | | union | | 联合类型 | | undefined | | 一个未定义或不存在的值 | | null | | 空 | | aliases | | 匿名类型 | | BigInt | | 任意大的数 | | void | | 没有任何返回值的类型 |

为了保证开发正确性和性能,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));  //string
console.log(typeof(msg2));  //string
console.log(typeof(msg22));  //string
console.log(typeof(msg3));  //object
console.log((msg === msg2)+"");  //true
console.log((msg === msg3)+"");  //false
console.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 //二进制 ES6
let 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) //二进制 ES6
let 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中也依旧存在,这里想起一张图。。。此处先只解决图里面精度丢失的问题,其它问题后续会出详细的文档来说明和解决。

00db12ce-d63f-4ee8-ae23-91872d48c05e.png

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

【鸿蒙版的bigNumber】
**问题原因:**可看这个https://juejin.cn/post/7216917459009536060

// Demo 1 - 乘法异常
console.log(18.9 * 100+"") // 1889.9999999999998
console.log(64.68 * 100+"") // 6468.000000000001

// Demo 2
// 典中典
console.log((0.1 + 0.2 === 0.3)+"") // false
console.log((0.1 === 0.1)+"") // true

....等等还有其它的例子
```typescript

## 4.2 Boolean类型
boolean类型由true和false两个逻辑值组成。  
通常在条件语句中使用boolean类型的变量:
```typescript
let 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)//object
console.log("当前数据类型2:" + typeof numberArray)//object
console.log("当前数据类型3:" + typeof numberArrayObj)//object
console.log("当前数据类型4:" + typeof tuple)//object
console.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)//number
console.log(ColorSet.Red.toString())//0
console.log(ColorSet.Green.toString())//1

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

enum ColorSet { Red = '红色', Green = '绿色', Blue = '蓝色'}
console.log(typeof ColorSet.Red)//string
console.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("姓名"))//string
console.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)+"")//true
console.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())//undefined
console.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);
}

// 推断的返回值类型为never
function 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()) // 2
console.log(m.has('k3')+"") // true
console.log(m.get('k3')+"") // v3
//遍历
m.forEach((value,key)=>{
  console.log(`key:${key} value:${value}`)
})
// key:k3 value:v3
// key:k4 value:v4
// keys
for (let key of m.keys()) {
  console.log(key)
}
// 'k3'
// 'k4'

// values
for (let val of m.values()) {
  console.log(val)
}
// 'v3'
// 'v4'

// delete、clear
console.log(m.delete('k3')+"") //true
console.log(m.has('k3')+"") // false
m.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')+"") // true
console.log(m.get('k3')+"") // v3
// delete、clear
console.log(m.delete('k3')+"") //true
console.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()) // 2
s.add('v3')
console.log(s.has('v3').toString()) // true

for (const v of s.keys()) {
  console.log(`keys:${v}`)
}
// keys:v1
// keys:v2
// keys:v3
for (const v of s.values()) {
  console.log(`values:${v}`)
}
// values:v1
// values:v2
// values:v3
s.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()) // false
s.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):检查集合中是否存在一个值。
// 创建一个 WeakSet
const 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;    // ok
x = null; // ok
if (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

//未赋值的空值属性,值为undefined
let nick: string | null
console.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 }
收藏00

登录 后评论。没有帐号? 注册 一个。