[TOC]
(本文档是在鸿蒙开发者文档的基础上,选取了最常用的一些关键语法,以及一些注释,可以帮助一些有前端/ios/Android开发经验的小伙伴快速掌握鸿蒙开发,本文档持续更新中.....)
ArkTS 语言
1.初识ArkTS 语言
1.ArKTS是什么
ArKTs是Harmony OS优选的主力应用开发语言。ArkTS围绕应用开发在TypeScript(简称TS)生态基础上做了进一步扩展,保持了TS的基本风格,同时通过规范定义强化开发期静态检查和分析,提升程序执行稳定性和性能。
(就是在TS的基础上增加了一些新的特性)
2.ArkTS的编程规范
1.类采用首字母大写的驼峰命名法,类名通常是名词或名词短语,例如Person、Student、Worker。
// 类名
class User {
username: string
constructor(username: string) {
this.username = username;
}
sayHi() {
console.log('hi' + this.username);
}
}
// 枚举名
enum UserType {
TEACHER = 0,
STUDENT = 1
};
2.函数的命名通常是动词或动词短语,采用小驼峰命名
let msg = 'Hello world';
function sendMsg(msg: string) {
// todo send message
}
let userName = 'Zhangsan';
function findUser(userName: string) {
// todo find user by user name
}
3.常量名、枚举值名采用全部大写,单词间使用下划线隔开
const MAX_USER_SIZE = 10000;
enum UserType {
TEACHER = 0,
STUDENT = 1
};
4.避免使用否定的布尔变量名
//错误命名
let isNoError = true;
let isNotFound = false;
function empty() {}
function next() {}
//正确命名
let isError = false;
let isFound = true;
function isEmpty() {}
function hasNext() {}
5.使用空格缩进,禁止使用tab字y
6.多个变量定义和赋值语句不允许写在一行
//反例
let maxCount = 10, isCompleted = false;
let pointX, pointY;
pointX = 10; pointY = 0;
//正确写法
let maxCount = 10;
let isCompleted = false;
let pointX = 0;
let pointY = 0;
7.数组遍历优先使用Array对象方法***
对于数组的遍历处理,应该优先使用Array对象方法,如:forEach(), map(), every(), filter(), find(), findIndex(), reduce(), some()。
//反例
const numbers = [1, 2, 3, 4, 5];
// 依赖已有数组来创建新的数组时,通过for遍历,生成一个新数组
const increasedByOne: number[] = [];
for (let i = 0; i < numbers.length; i++) {
increasedByOne.push(numbers[i] + 1);
}
//正确写法
const numbers = [1, 2, 3, 4, 5];
// better: 使用map方法是更好的方式
const increasedByOne: number[] = numbers.map(num => num + 1);
8.不要在控制性条件表达式中执行赋值操作
控制性条件表达式常用于if、while、for、?:等条件判断中。
在控制性条件表达式中执行赋值,常常导致意料之外的行为,且代码的可读性非常差。
//反例
// 在控制性判断中赋值不易理解
if (isFoo = false) {
...
}
//正确写法
const isFoo = someBoolean; // 在上面赋值,if条件判断中直接使用
if (isFoo) {
...
}
9.使用T[]表示数组类型
ArkTS提供了两种数组类型的表示方式:T[]和Array。为了代码的可读性,建议所有数组类型均用T[]来表示。
//反例
let x: Array<number> = [1, 2, 3];
let y: Array<string> = ['a', 'b', 'c'];
// 统一使用T[]语法
let x: number[] = [1, 2, 3];
let y: string[] = ['a', 'b', 'c'];
.......
3.从Ts到ArkTS的注意点
背景:
动态类型语言,例如JavaScript(简称JS),可以使得开发者非常快速地编写代码,但是同时,它也使得程序容易在运行时产生非预期的错误。例如在代码中,如果开发者没有检查一个值是否为undefined,那么程序有可能在运行时崩溃,给开发者造成不便。如果能在代码开发阶段检查此类问题是更有好处的。TS通过标注类型帮助开发者检查错误,许多错误在编译时可以被编译器检测出来,不用等到程序运行时。但是,即使是TS也有局限性,它不强制要求对变量进行类型标注,导致很多编译时检查无法开展。ArkTS尝试克服这些缺点,它强制使用静态类型,旨在通过更严格的类型检查以减少运行时错误。
1.显式初始化类的属性
//非严格模式下的TS代码。
class Person {
name: string // 可能为undefined
setName(n: string): void {
this.name = n
}
getName(): string {
// 开发者使用"string"作为返回类型,这隐藏了name可能为"undefined"的事实。
// 更合适的做法是将返回类型标注为"string | undefined",以告诉开发者这个API所有可能的返回值的类型。
return this.name
}
}
let buddy = new Person()
// 假设代码中没有对name的赋值,例如没有调用"buddy.setName('John')"
buddy.getName().length; // 运行时异常:name is undefined
//由于ArkTS要求属性显式初始化,代码应该像下面这样写。
class Person {
name: string = ''//显式声明
setName(n: string): void {
this.name = n
}
// 类型为"string",不可能为"null"或者"undefined"
getName(): string {
return this.name
}
}
let buddy = new Person()
// 假设代码中没有对name的赋值,例如没有调用"buddy.setName('John')"
buddy.getName().length; // 0, 没有运行时异常
。。。。
2.ArkTS的基本语法
1.声明
ArkTS中的console.log()第一个参数只能打印string类型与js有所不同需要注意一下
static log(message: string, ...arguments: any[]): void;
1.变量声明
let hi: string = 'hello';
hi = 'hello, world';
2.常量声明
const hello: string = 'hello';
3.自动类型推断
let hi1: string = 'hello';
let hi2 = 'hello, world';
2.类型
1.Number类型
2.Boolean类型
3.String类型
4.Void类型
5.Object类型 (Object类型是所有引用类型的基类型。任何值,包括基本类型的值(它们会被自动装箱),都可以直接被赋给Object类型的变量 )
let x:object = new Number(1)
6.Array类型
7.Enum类型
enum ColorSet { Red, Green, Blue }
let c: ColorSet = ColorSet.Red;
console.log('枚举',JSON.stringify(c) )
// 枚举 {"0":"Red","1":"Green","2":"Blue","Red":0,"Green":1,"Blue":2}
enum ColorSet { White = 0xFF, Grey = 0x7F, Black = 0x00 }
let c: ColorSet = ColorSet.Black;
console.log('枚举',c)
// 枚举 0(16进制)
8.Union类型
class Cat {
name: string = 'cat';
// ...
}
class Dog {
name: string = 'dog';
// ...
}
class Frog {
name: string = 'frog';
// ...
}
type Animal = Cat | Dog | Frog | number;
// Cat、Dog、Frog是一些类型(类或接口)
let animal: Animal = new Cat();
animal = new Frog();
animal = 42;
// 可以将类型为联合类型的变量赋值为任何组成类型的有效值
9.type类型别名
含义:类型别名,顾名思义就是给某个类型起别名,之后就可以通过这个别名来使用类型啦。咱们开发中用到的一些内置类型就是通过 type 来定义的哦
// 定义类型别名
type IDType = string | number
// 使用类型别名
function printID(id:IDType ) {
console.log(id+'')
}
// 调用函数
printID(10)
printID('20')
@Entry
@Component
struct Page01_type {
@State message: string = 'Type类型别名';
build() {
Row() {
Column() {
Text(this.message)
.fontSize(30)
.fontWeight(FontWeight.Bold)
}
.width('100%')
}
.height('100%')
}
}
10.typeof运算符
可以通过 typeof 运算符来获取类型,他返回的是一个字符串
// 前面 5 个可以正常获取到类型
console.log(typeof 123) // number
console.log(typeof '123') // string
console.log(typeof false) // boolean
console.log(typeof undefined) // undefined
function func() {
}
console.log(typeof func) // function
interface Person{
name:string
}
// 对象 数组 null 获取到的都是 object
const p: Person = {name:'jack'}
console.log(typeof null) // object
console.log(typeof [1, 2, 3]) // object
console.log(typeof p) // object
3.运算符
(和前端开发的运算符基本一样☺)
1.赋值运算符
赋值运算符=,使用方式如x=y。
复合赋值运算符将赋值与运算符组合在一起,其中x op = y等于x = x op y。
复合赋值运算符列举如下:+=、-=、*=、/=、%=、<<=、>>=、>>>=、&=、|=、^=。
2.比较运算符
| 运算符 | 说明 |
|---|---|
| === | 如果两个操作数严格相等(对于不同类型的操作数认为是不相等的),则返回true。 |
| !== | 如果两个操作数严格不相等(对于不同类型的操作数认为是不相等的),则返回true。 |
| == | 如果两个操作数相等,则返回true。 |
| != | 如果两个操作数不相等,则返回true。 |
| 如果左操作数大于右操作数,则返回true。 | |
| >= | 如果左操作数大于或等于右操作数,则返回true。 |
| < | 如果左操作数小于右操作数,则返回true。 |
| <= | 如果左操作数小于或等于右操作数,则返回true。 |
3.算术运算符
一元运算符为-、+、--、++。
二元运算符列举如下:
| 运算符 | 说明 |
|---|---|
| + | 加法 |
| - | 减法 |
| * | 乘法 |
| / | 除法 |
| % | 除法后余数 |
4.位运算符
| 运算符 | 说明 |
|---|---|
| a & b | 按位与:如果两个操作数的对应位都为1,则将这个位设置为1,否则设置为0。 |
| a | b | 按位或:如果两个操作数的相应位中至少有一个为1,则将这个位设置为1,否则设置为0。 |
| a ^ b | 按位异或:如果两个操作数的对应位不同,则将这个位设置为1,否则设置为0。 |
| ~ a | 按位非:反转操作数的位。 |
| a << b | 左移:将a的二进制表示向左移b位。 |
| a >> b | 算术右移:将a的二进制表示向右移b位,带符号扩展。 |
| a >>> b | 逻辑右移:将a的二进制表示向右移b位,左边补0。 |
5.逻辑运算符
| 运算符 | 说明 |
|---|---|
| a && b | 逻辑与 |
| a || b | 逻辑或 |
| ! a | 逻辑非 |
4.语句
1.if语句
2.Switch语句
switch (expression) {
case label1: // 如果label1匹配,则执行
// ...
// 语句1
// ...
break; // 可省略
case label2:
case label3: // 如果label2或label3匹配,则执行
// ...
// 语句23
// ...
break; // 可省略
default:
// 默认语句
}
3.条件表达式
condition ? expression1 : expression2
如果condition的为真值(转换后为true的值),则使用expression1作为该表达式的结果;否则,使用expression2。
4.For语句
5.For-of语句
使用for-of语句可遍历数组或字符串。示例如下:
for (let ch of 'a string object') {
/* process ch */
}
6.While语句
7.Do-while语句
如果condition的值为真值(转换后为true的值),那么statements语句会重复执行。示例如下:
do {
statements
} while (condition)
8.Break语句 :使用break语句可以终止循环语句或switch。
9.Continue语句 :continue语句会停止当前循环迭代的执行,并将控制传递给下一个迭代。
10.Throw和Try语句
---------------------------------------------------第一次更新完毕-----------------------------------------
5.函数
1.函数声明
//函数声明引入一个函数,包含其名称、参数列表、返回类型和函数体。
function add(x: string, y: string): string {
let z: string = `${x} ${y}`;
return z;
}
2.可选参数
//?表示参数可以有也可以没有
function hello(name?: string) {
if (name == undefined) {
console.log('Hello!');
} else {
console.log(`Hello, ${name}!`);
}
}
//设置默认值
function multiply(n: number, coeff: number = 2): number {
return n * coeff;
}
multiply(2); // 返回2*2
multiply(2, 3); // 返回2*3
3.Rest参数
function sum(...numbers: number[]): number {
let res = 0;
for (let n of numbers)
res += n;
return res;
}
sum(); // 返回0
sum(1, 2, 3); // 返回6
展开运算符
出于程序稳定性,以及运行性能考虑,在 ArkTS 中 ...(展开运算符) 只能用在数组上
(注:TS中 ... 数组和对象均可以使用 )
const numArr1: number[] = [1, 2, 3, 4]
const numArr2: number[] = [5, 6, 7]
// 合并到一起
const totalArr: number[] = [...numArr1, ...numArr2]
// 添加
const numArr3:number[] = [8,9,10]
const numArr4:number[] = [11,12,13]
// 将 numArr4 展开,传递给push
numArr3.push(...numArr4)
4.返回值
// 显式指定返回类型
function foo(): string { return 'foo'; }
// 推断返回类型为string
function goo() { return 'goo'; }
5.函数作用域
函数中定义的变量和其他实例仅可以在函数内部访问,不能从外部访问。
如果函数中定义的变量与外部作用域中已有实例同名,则函数内的局部变量定义将覆盖外部定义。
6.函数调用
function join(x: string, y: string): string {
let z: string = `${x} ${y}`;
return z;
}
let x = join('hello', 'world');
console.log(x); //hello world
7.声明函数类型(一般用于定义回调函数)
// 这是一个函数类型
type trigFunc = (x: number) => number
function do_action(f: trigFunc) {
//在ArkTS中函数的参数一定要限制
console.log('绝对值为',f(-1))
}
do_action(Math.abs); // 将函数作为参数传入
8.箭头函数(又名Lambda函数)t
1.函数可以定义为箭头函数(要记得用变量接收)
let sum = (x: number, y: number): number => {
return x + y;
}
2.箭头函数的返回类型可以省略;省略时,返回类型通过函数体推断。
3.表达式可以指定为箭头函数,使表达更简短,因此以下两种表达方式是等价的:
let sum1 = (x: number, y: number) => { return x + y; }
let sum2 = (x: number, y: number) => x + y
9.闭包
(闭包的本质就是想在函数的外部拿到函数内部的值,这就需要返回一个函数,并在该函数的内部来调用需要拿到的变量从而进行锁定变量)
function f(): () => number {
let count = 0;
let g = (): number => { count++; return count; };
//我拿到了 count并把他返回出去从而就劫持了函数内部的count变量
return g;
}
let z = f();
z(); // 返回:1
z(); // 返回:2
6.类
类是用于创建对象的模板。他们用代码封装数据以处理该数据。同时类声明也会引入一个新类型,并定义其字段、方法和构造函数。
// 类名 首字母大写(规范)
class 类名{
// 字段
// 方法
// 构造函数
}
// 使用类 实例化对象 基于类 创建对象
const x:类名 = new 类名()
示例
类声明引入一个新类型,并定义其字段、方法和构造函数。
//在以下示例中,定义了Person类,该类具有字段name和surname、构造函数和方法fullName:
class Person {
name: string = '';
surname: string = '';
constructor (n: string, sn: string) {
this.name = n;
this.surname = sn;
}
fullName(): string {
return this.name + ' ' + this.surname;
}
}
//定义类后,可以使用关键字new创建实例:
let p = new Person('John', 'Smith');
console.log(p.fullName());
可以使用对象字面量创建实例
class Point {
//默认是使用public修饰的
x: number = 0;
y: number = 0;
}
let p: Point = {x: 42, y: 42};
//另一种写法,此时已经实例化加赋值了不能加参数了
let p1:Point = new Point()
全面了解ArkTS的类
-
实例属性
通过实例属性(字段)来保存各种类型的数据
// 类
class 类名{
// 字段名+类型+初始值
字段名:类型='xxx'
// 可选字段可以不设置初始值
字段名?:类型
}
// 可选字段在使用时需要配合 可选链操作符 避免出错
参考代码
class Person {
name: string = 'jack'
food?: string
}
const p = new Person()
p.name = 'jack'
console.log(p.name)
console.log('', p.food?.length)
- 构造函数
代码在实例化 之后,挨个对属性进行赋值,如果自定义了构造函数,可以在构造函数中完成该操作
class 类{
字段A:类型
字段B:类型
constructor(参数...) {
// 通过 new 实例化的时候 会调用 constructor
// 通过关键字 this 可以获取到实例对象
}
}
const 实例 = new 类()
//------------------案例
interface IFood {
name: string
price: number
}
class Food {
name: string
price: number
// constructor(name:string,price:number) {
// this.name=name
// this.price = price
// }
constructor(value: IFood) {
this.name = value.name
this.price = value.price
}
}
// const f = new Food('西兰花炒蛋', 15)
const f = new Food({ name: '花菜炒蛋', price: 10 })
3.实例方法
类中可以定义 方法,并且在内部编写逻辑.这种方法需要通过实例化的对象调用-称之为实例方法
class 类名{
方法名(参数...):返回值类型{
// 逻辑...
// 可以通过 this 获取实例对象
}
}
-------------------------案例
class Person{
name:string
constructor(name:string) {
this.name = name
}
sayHi(name:string){
console.log(`你好${name},我的名字是:${this.name}`)
}
}
const p:Person = new Person('jack')
p.sayHi('rose')
4.静态属性,方法
类还可以添加静态属性、方法,后续访问需要通过 类 来完成
// 定义
class 类{
static 字段:类型
static 方法(){}
}
// 使用
类.字段
类.方法()
//试一试
class Person{
static staticField:string ='静态字段'
static staticMethod(){
console.log('静态方法')
}
}
Person.staticField
Person.staticMethod()
5.继承,super关键字
类可以通过 继承 快速获取另外一个类的 字段 和 方法。
class 父类 {
// 字段
// 方法
// 构造函数
}
class 子类 extends 父类{
// 自己的字段(属性)
// 自己的方法
// 可以重写父类方法
}
子类通过 super 可以访问父类的实例字段、实例方法和构造函数。可以在适当的时候使用
class 父类 {
func(){
}
}
class 子类 extends 父类 {
constructor() {
super() // 调用父类构造函数
}
方法(){
super.方法() // 调用父类方法
}
}
class Person {
name: string
age: number
sayHi() {
console.log(`你好,我叫:${this.name}`)
}
constructor(name: string, age: number) {
this.name = name
this.age = age
}
}
class Student extends Person {
height: number
constructor(name: string, age: number, height: number) {
// name 和 age 的初始化通过 super 来调用父类的构造函数
super(name, age)
this.height = height
}
sayHi(): void {
// 通过 super 访问父类的 字段 方法
super.sayHi()
super.name
super.age
console.log('子类的 sayHi')
}
}
const s: Student = new Student('jack', 18, 170)
6.instanceof
instanceof 运算符可以用来检测某个对象是否是某个类的实例
// 返回判断结果 布尔值
实例对象 instanceof Class
//试一试
class Person {
name: string = ''
}
class Student extends Person {
age: number
constructor(age: number) {
super()
this.age = age
}
}
const s = new Student(10)
console.log('isStudent', s instanceof Student)
console.log('isPerson', s instanceof Person)
console.log('isArray', [1, 2, 3,] instanceof Array)
const arr = [1, 2, 3]
// 等同于
const arr2 = new Array(1, 2, 3)
7.修饰符
a.readonly(只读)
readonly 的意思是只读,可以用来修饰属性(字段),修饰之后外部只可以取值,无法修改
b.private(私有)
private修饰的成员不能在声明该成员的类之外访问,包括子类
c.protected(受保护的)
protected修饰符的作用与private修饰符非常相似,不同点是protected修饰的成员允许在派生类(子类) 中访问
d.public(公共)
public修饰的类成员(字段、方法、构造函数)在程序的任何可访问该类的地方都是可见的
7.接口
概述
1.接口声明引入新类型。接口是定义代码协定的常见方式。
2.任何一个类的实例只要实现了特定接口,就可以通过该接口实现多态。
3.接口通常包含属性和方法的声明
interface Style {
color: string; // 属性
}
interface AreaSize {
calculateAreaSize(): number; // 方法的声明
someMethod(): void; // 方法的声明
}
实现接口的类示例:
// 接口:
interface AreaSize {
calculateAreaSize(): number; // 方法的声明
someMethod(): void; // 方法的声明
}
// 实现:
class RectangleSize implements AreaSize {
private width: number = 0;
private height: number = 0;
someMethod(): void {
console.log('someMethod called');
}
calculateAreaSize(): number {
this.someMethod(); // 调用另一个方法并返回结果
return this.width * this.height;
}
}
接口继承
接口继承使用的关键字是 extends
interface 接口1{
属性1:类型
}
interface 接口2 extends 接口1 {
属性2:类型
}
//IVegetable继承了IFood的属性
interface IFood {
name: string
}
interface IVegetable extends IFood {
color: string
}
const flower: IVegetable = {
name: '西兰花',
color: '黄绿色'
}
接口的实现
可以通过接口结合 implements 来限制 类 必须要有某些属性和方法
interface 接口{
属性:类型
方法:方法类型
}
class 类 implements 接口{
// 必须实现 接口中定义的 属性、方法,否则会报错
}
//通过接口限制类可以使得类非常的稳定
interface IDog {
name: string
bark: () => void
}
class Dog implements IDog {
name: string = ''
food: string = ''
bark() {
}
}
3.ArkTS的进阶语法
1. 泛型
泛型在保证类型安全(不丢失类型信息)的同时,可以让函数等与多种不同的类型一起工作,灵活可复用
**通俗一点就是:类型是可变的!
1.泛型函数
顾名思义就是,泛型和函数结合到一起使用
Type 是泛型参数的名字,类似于之前的形参,
-
可以自行定义,有意义即可
-
首字母大写
-
参见泛型参数名 T、Type
// 函数定义 function identity<Type>(arg: Type): Type { return arg; } // 在使用的时候传入具体的类型,函数就可以正常工作啦~ identity<string>('123') identity<number>(123) identity<boolean>(false) identity<string[]>(['1', '2', '3'])结合 编译器 的 类型推断 功能,在使用函数的时候还可以进行简写,比如下面的写法,和上面的是一样的。
虽然大部分时候可以推断出类型,但是如果碰到 编译器 无法推断类型时,就需要显式传入类型参数,这在更复杂的示例中可能会发生。
identity('123')
identity(123)
identity(false)
identity(['1', '2', '3'])
使用泛型变量
类型变量可以用在任意支持的位置,实现更为复杂的功能
//将类型变量 Type,作为数组项的类型即可
function identity<Type>(arr: Type[]): number {
return arr.length
}
2.使用泛型约束
如果开发中不希望任意的类型都可以传递给 **类型参数 ,**就可以通过泛型约束来完成
核心步骤:
-
定义用来约束的 接口(interface)
-
类型参数通过 extends 即可实现约束
interface 接口{ 属性:类型 } function 函数<Type extends 接口>(){} // 后续使用函数时,传入的类型必须要有 接口中的属性 ------试一试 interface ILengthwise { length: number } function identity<Type extends ILengthwise>(arr: Type) { console.log(arr.length.toString()) } // 使用的时候 只要有 length 属性即可 identity([1, 2, 3, 4]) // 数组有 length 属性 正常运行 identity('1234') // 字符串也有 length 属性 正常运行 // identity(124) // 数值没有 length 属性 报错 class Cloth implements ILengthwise { length: number = 10 } class Trousers { length: number = 110 } identity(new Cloth()) // Cloth 有 length 属性 正常运行 identity(new Trousers()) // Trousers 有 length 属性 正常运行
3.多个泛型参数
日常开发的时候,如果有需要可以添加多个 类型变量,只需要定义并使用 多个类型变量即可
function funcA<T, T2>(param1: T, param2: T2) {
console.log('参数 1', param1)
console.log('参数 2', param2)
}
funcA<string, number>('张三', 18)
funcA<string[], boolean[]>(['张三','李四'], [false])
4.泛型接口
定义接口时结合泛型,那么这个接口就是 泛型接口
interface 接口<Type>{
// 内部使用Type
}
interface IdFunc<Type> {
id: (value: Type) => Type
ids: () => Type[]
}
let obj: IdFunc<number> = {
id(value) { return value },
ids() { return [1, 3, 5] }
}
5.泛型类
和泛型接口类似,如果定义类的时候结合泛型,那么这个类就是 泛型类
class 类名<Type>{
// 内部可以使用 Type
}
-----试一试
// 定义
class Person <T> {
id: T
constructor(id: T) {
this.id = id
}
getId(): T {
return this.id
}
}
// 使用
let p = new Person<number>(10)
2.工具类型
ArkTS提供了4 中工具类型,来帮组我们简化编码
4 种工具类型中,熟练掌握 Partial 即可,可以简化编码
1.partial
(partial:部分)
基于传入的Type类型构造一个[新类型],将Type的所有属性设置为可选。
(泛型约束太复杂了这个完全可以替代他)
type 新类型 = Partial<接口>
type 新类型 = Partial<类>
// 后续使用新类型即可
interface Person {
name: string
age: number
friends: string[]
}
type ParPerson = Partial<Person>
// 因为都是可选的,可以设置为空对象
let p: ParPerson = {}
2.Required
基于传入的Type类型构造一个【新类型】,将 Type 的所有属性设置为必填
type 新类型 = Required<接口>
type 新类型 = Required<类>
// 后续使用新类型即可
class Person {
name?: string
age?: number
friends?: string[]
}
type RequiredPerson = Required<Person>
// 都是必须得属性,必须设置值
let p: Required<Person> = {
name: 'jack',
age: 10,
friends: []
}
3.Readonly
基于 Type构造一个【新类型】,并将Type 的所有属性设置为readonly
type 新类型 = Readonly<接口>
type 新类型 = Readonly<类>
// 后续使用新类型即可
class Person {
name: string = ''
age: number = 0
}
type ReadonlyIPerson = Readonly<Person>
const p: ReadonlyIPerson = {
name: 'jack',
age: 10
}
p.name = 'rose' // 报错 属性全部变成只读
4.Record<Keys,Type>
构造一个对象类型,其属性键为Keys,属性值为Type。该实用程序可用于将一种类型的属性映射到另一种类型。
class CatInfo {
age: number = 0
breed: string = ''
}
// 联合类型
type CatName = "miffy" | "boris" | "mordred";
// 通过Record 构建新的对象类型
// 属性名:必须是CatName 中的值
// 属性值:必须是CatInfo 类型
type Ctype = Record<CatName, CatInfo>
const cats: Ctype = {
'miffy': { age: 5, breed: "Maine Coon" },
'boris': { age: 5, breed: "Maine Coon" },
'mordred': { age: 16, breed: "British Shorthair" },
};
3.空安全
默认情况下,ArkTS中的所有类型都是不可为空的。如果要设置为空,需要进行特殊的处理,并且在获取 可能为空的值的时候也需要特殊处理
1.联合类型设置为空
let x: number = null; // 编译时错误
let y: string = null; // 编译时错误
let z: number[] = null; // 编译时错误
通过联合类型指定为空即可,使用时可能需要判断是否为空
// 通过联合类型设置为空
let x: number | null = null;
x = 1; // ok
x = null; // ok
// 取值的时候,根据需求可能需要考虑 屏蔽掉空值的情况
if (x != null) { /* do something */ }
2.非空断言运算符
后缀运算符! 可用于断言其操作数为非空。
应用于空值时,运算符将抛出错误。否则,值的类型将从T | null更改为T:
let x: number | null = 1;
let y: number
y = x + 1; // 编译时错误:无法对可空值作加法
y = x! + 1; // 通过非空断言,告诉编译器 x不为 null
3.空值合并运算符
空值合并二元运算符?? 用于检查左侧表达式的求值是否等于null。如果是,则表达式的结果为右侧表达式;否则,结果为左侧表达式。
换句话说,a ?? b等价于三元运算符a != null ? a : b。
在以下示例中,getNick方法如果设置了昵称,则返回昵称;否则,返回空字符串:
class Person {
name: string | null = null
getName(): string {
// return this.name === null ? '' : this.name
// 等同于 如果 name不为空 就返回 name 反之返回 ''
return this.name ?? ''
}
}
4.可选链
在访问对象属性时,如果该属性是undefined或者null,可选链运算符会返回undefined。
(可选链?当前面的对象不为空或undefined才调用属性或方法)
class Dog {
bark() {
console.log('啊呜~')
}
}
class Person {
name?: string
dog?: Dog
constructor(name: string, dog?: Dog) {
this.name = name
this.dog = dog
}
}
const p: Person = new Person('jack')
// p.dog.bark()// 报错 dog 可能为空
// 逻辑判断
if (p.dog) {
p.dog.bark()
}
// 当 dog不为null 或 undefined 时 再调用 bark 并且不会报错
p.dog?.bark()
4.模块化
- 把一个大的程序拆分成【互相依赖】的若干小文件
- 这些小文件还可以通过【特定的语法】组合到一起
- 这个过程称之为【模块化】
优点:
1.更好维护
2.更好的复用性
缺点:
需要学习模块化语法
分析:
- 功能写完只有10行代码,模块化没啥必要!
- 功能写完有100行,或者1000行代码,里面有【好几段逻辑】在其他地方也要用--模块化
1.默认导入与导出
ArkTS 中每个 ets 文件都可以看做是一个模块,默认只能在当前文件中使用,如果要在其他位置中使用就需要:
-
当前模块中 导出模块
-
需要使用的地方 导入模块
// 默认导出
export default 需要导出的内容
// 默认导入
import xxx from '模块路径'
//试一试
// Model.ets
// 一起写
export default class Person {
name: string = ''
}
// 分开写
// export default Person
// 只能有一个默认导出
// TestModel.ets
import Person from './model'
const p: Person = new Person()
路径
/ 表示进入某个文件夹里面
. 表示当前文件所在文件夹 ./
.. 表示当前文件的上一级文件夹 ../
2.按需导入与导出
如果有很多的东西需要导出,可以使用按需导出,他也有配套的导入语法
// ---------- Model.ets ----------
const info: string = '信息'
// 单独写
export const num: number = 10
function sayHi() {
console.log('你好吗~')
}
// 或者 写到 {} 内部
export {
info, sayHi
}
// ---------- MainFile.ets ----------
import { info, num, sayHi as sayHello } from './Model'
console.log('info:', info)
// 起别名
sayHello()
console.log('num:', num + '')
3.全部导入
导出部分不需要调整,调整导入的语法即可
import * as Utils from './utils'
// 通过 Utils 即可获取 utils模块中导出的所有内容
5.定时器
日常开发中如果需要代码 延迟一会执行,或者每隔一段时间执行一次,就可以使用定时器
定时器有两种:
- setTimeout: 延迟执行
- setInterval: 间隔执行
1.setTimeout(延迟执行)
setTimeout可以用来设置一个定时器,当设置的时间到了之后就会去执行指定的函数
执行一次用 setTimeout
setTimeout、clearTimeout是一个全局函数,可以直接使用
@Entry
@Component
struct Page01_timeoutAndInterval {
timeId?: number
build() {
Column({ space: 20 }) {
Text('延时器-Timeout')
.fontSize(30)
.width('100%')
.textAlign(TextAlign.Center)
Row({ space: 20 }) {
Button('开启延时器')
.onClick(() => {
// 开启延时器
let timeId = setTimeout(() => {
// 延迟执行逻辑
console.log('代码执行啦')
}, 2000)
// 保存定时器 id
this.timeId = timeId
})
Button('关闭延时器')
.onClick(() => {
// 调用 clearTimeout 清除定时器
clearTimeout(this.timeId)
})
}
}
.padding(10)
}
}
2.setInterval(间隔执行)
setInterval 也是可以用来设置一个定时器,根据设置的时间间隔来执行指定的函数
执行多次用 setInterval!!
setInterval、clearInterval是一个全局函数,可以直接使用
// 1. 基本使用
setInterval(()=>{
// 延迟执行的逻辑
},时间)
// 2. 获取返回的定时器 id
const timeId = setInterval(()=>{
// 延迟执行的逻辑
},时间)
// 3. 根据定时器 id 清除定时器
clearInterval(timeId)
//试一试
@Entry
@Component
struct Page02_interval{
// 只是用来保存数据,不影响 UI 可以不加状态修饰符
timeId?: number
build() {
Column({ space: 20 }) {
Text('定时器-Interval')
.fontSize(30)
.width('100%')
.textAlign(TextAlign.Center)
Row({ space: 20 }) {
Button('开启延时器')
.onClick(() => {
this.timeId = setInterval(() => {
console.log('代码执行啦~')
}, 2000)
})
Button('关闭延时器')
.onClick(() => {
clearInterval(this.timeId)
})
}
}
.padding(10)
}
}
6.递归
调用自身的函数我们称之为递归函数。
基本用法
在某种意义上说,递归近似于循环。两者都重复执行相同的代码,并且两者都需要一个终止条件(避免无限循环,或者在这种情况下更确切地说是无限递归)。
// 没有退出条件的 递归函数--无限递归
function func(){
func()
}
//试一试
let num: number = 1
function printLog(num: number) {
console.log(`你好,第${num}次打印`)
// 递减
num--
// 退出条件
if (num > 1) {
printLog(num)
}
}
// 打印 10 次
printLog(10)
7.常用内置对象
1.Math对象
是一个内置对象,它拥有一些数学常数属性和数学函数方法。Math 的所有属性与方法都是静态的,使用的时候直接通过Math点出来即可
常用方法
| 方法 | 说明 |
|---|---|
| Math.random() | 返回一个 0 到 1 之间的伪随机数。 |
| Math.ceil(x) | 返回大于一个数的最小整数,即一个数向上取整后的值。 |
| Math.floor(x) | 返回小于一个数的最大整数,即一个数向下取整后的值。 |
| Math.round(x) | 返回四舍五入后的整数。 |
| Math.abs(x) | 返回一个数的绝对值 |
| Math.max([x[,y[, …]]]) | 返回零到多个数值中最大值。 |
| Math.min([x[,y[, …]]]) | 返回零到多个数值中最小值。 |
| Math.Pi | 圆周率,一个圆的周长和直径之比,约等于 3.14159。 |
| ....更多内容参考文档 |
const numA: number = 1.5
console.log(Math.ceil(numA) + '') // 向上取整 2
console.log(Math.floor(numA) + '') // 向下取整 1
console.log(Math.round(numA) + '') // 四舍五入 2
const numB:number = -9
console.log(Math.abs(numB) + '') // 绝对值 9
const numList: number[] = [13, 2, 31, 42, 15, 56, 27, 28]
const max: number = Math.max(...numList)
const min: number = Math.min(...numList)
console.log('max:', max) // 最大值
console.log('min:', min) // 最小值
// 0-1 取得到0,取不到 1
console.log(Math.random() + '')
// 返回 0-n的随机数的函数
function getRandomArbitrary(max: number): number {
return Math.floor(Math.random() * (max + 1))
}
// 返回 min-max 的随机数的函数
function getRandomIntInclusive(min: number, max: number): number {
return Math.floor(Math.random() * (max - min + 1)) + min; //含最大值,含最小值
}
2.Date对象
ArkTS 中另外一个常用的内置对象 Date,他可以用来创建、解析、操作日期和时间。
使用 Date 对象首先需要通过 new 操作符进行实例化
// 获取当前日期
const date1: Date = new Date()
// 获取指定日期
// ISO 8601 格式(YYYY-MM-DDTHH:mm:ss.ssZ)中间用 T 分隔
const date2: Date = new Date('1995-01-01T01:11:00')
// Unix时间戳 是指从1970年1月1日(UTC)开始到现在经历的时间(毫秒)
const date3: Date = new Date(1706170405708)
实例方法
| 方法名 | 作用 | 说明 |
|---|---|---|
| getFullYear | 获取年份 | 4 位数年份 |
| getMonth | 获取月份 | 取值 0-11 |
| getDate | 获取日期 | 月份中的日期 |
| getHours | 获取小时 | 无 |
| getMinutes | 获取分钟 | 无 |
| getSeconds | 获取秒 | 无 |
| getDay | 获取星期 | 周日为 0 |
静态方法
| 方法名 | 作用 | 说明 |
|---|---|---|
| now | 获取当前时间 | 时间戳 |
// 大伙执行的时候 即可获取到时间戳啦~
console.log(Date.now()+'')
8.String字符串对象
String 提供了不少的方法让我们来处理字符,咱们来看几个常用的
1.split(分隔)
split() 方法根据传入的内容将字符串分隔为数组
字符串.split(分隔符)// 返回切割之后的数组
--------试一试
// 1. 切割为数组
const branchFilter: string = ' 全部分路 | 对抗路 | 中路 | 发育路 | 游走 | 打野 '
console.log('测试',branchFilter.split('|'))
// 全部分路 , 对抗路 , 中路 , 发育路 , 游走 , 打野
const tabFilter: string = ' 所有段位 - 巅峰赛1350+ - 顶端排位 - 赛事 '
console.log('测试',tabFilter.split('-'))
// 所有段位 , 巅峰赛1350+ , 顶端排位 , 赛事
// 2. 获取日期
const dateStr:string = '2024-04-27'
console.log('测试',dateStr.split('-'))
//2024,04,27
2.trim(去空格)
trim方法会从字符串的两端移除空白字符,并返回一个【新的字符串】,而不会修改原始字符串。
字符串.trim()// 返回去除空格之后的字符串
// trim 去除两边空格
// 1.基础文本
const str: string = ' 123 '
console.log(str.trim()) // 123
// 2.用户名
const username = ' jack '
// 3.密码
const password = ' 1234abc '
3.toLowerCase(转小写)和toUpperCase(转大写)
toLowerCase() 方法将该字符串转换为小写形式。toUpperCase() 方法将该字符串转换为大写形式。
字符串.toLowerCase()// 返回转为小写之后的字符串
字符串.toUpperCase()// 返回转为大写之后的字符串
-----------应用场景
// toLowerCase 转小写 toUpperCase 转大写
// 1.验证码
const code = '1a2C'
// 2.带后缀名的文件信息
const filename = 'abc.PNG'
// 3.编程语言中的关键字
const boolStr = 'true'
4.includes(判断是否存在)
includes() 方法执行区分大小写的搜索,以确定是否可以在一个字符串中找到另一个字符串,并根据情况返回 true 或 false。
字符串.includes(查询的字符串)// 返回判断结果 true / false
-------------------应用场景
// includes 方法 判断是否存在特定字符
// 1. 判断是否有@符号
const emailStr = 'user@example.com'
// 2.判断是否为gif文件
const fileName = 'dance.gif'
// 3.判断密码中是否有下划线
const pass = 'itheima-123'
// 4.判断是否有特定词汇 ,比如 牛逼 最 等
const comment = `杭州是一个令人惊叹的地方,我刚刚度过了一个超牛逼的旅行。
西湖的景色太美了,特别是在夕阳西下时,湖水波光粼粼,真是美得不像话。
还去了灵隐寺,感受到了古老的气息和庄严的氛围,让我心生敬意。
在杭州还尝了最正宗的龙井茶和小笼包,老牛逼了!
最喜欢的是游览乌镇古镇,那里的古建筑和石板路让我仿佛穿越回古代。这次旅行真是太棒了,杭州是个值得一游的地方!`
5.slice(提取)
slice方法提取字符串的一部分,并将其作为新字符串返回,而不修改原始字符串。
字符串.slice(起始索引)// 从起始索引切割到末尾
字符串.slice(起始索引,结束索引) // 从起始索引,切换到结束索引
----------应用场景
// slice方法
// 1. 提取 逗号 之后的内容
const str1 = 'hi,你好吗?'
// 2. 提取 中午
const str2 = '今天中午吃的是西兰花炒蛋'
9.数组的常用方法
数组都是 Array 的实例化对象,它提供了很多的 方法来帮助我们快速处理数据
下表列出了一些较为常用方法:
| 方法名 | 作用 |
|---|---|
| indexof | 判断元素在数组中的索引,不存在返回-1 |
| join | 根据传入内容拼接数组,默认为,返回拼接结果,字符串 |
| forEach | 遍历数组,并且将数组的每一项作为参数传入到回调函数中,无返回值 |
| map | 基于原数组,创建一个新数组 |
| filter | 根据回调函数的执行结果,筛选出符合要求的元素,返回一个新数组 |
| find | 返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined |
| findIndex | 返回数组中满足条件第一个元素的索引。若没有找到则返回 -1。 |
| every | 返回数组中是否每一个元素都符合要求。 |
| some | 返回数组中是否有一个元素符合要求。 |
| sort | 方法的作用是对原数组的元素进行排序 |
| reduce | 数组求和 |
..........还有一些其他的方法,之后的课程中陆续补充
1.indexOf方法
indexOf() 方法返回数组中第一次出现给定元素的下标,如果不存在则返回 -1。
数组.indexof(要查找的元素)// 存在返回具体下标,不存在返回 -1
-------应用
// 1. 数字数组
const nums = [1,2,3,4,5]
// 2. 字符串数组
const foods = ['西兰花','西葫芦','西兰花']
2.join方法
**join()**方法将数组用逗号或指定的分隔符,将数组的每一项拼接到一起
数组.join('分隔符') // 基于传入的分隔符 将数组的每一项拼接到一起
数组.join() // 使用【逗号】将数组的每一项 拼接到一起
//试一试
const elements = ['花菜', '西兰花', '菜花']
console.log(elements.join()) // 默认拼接!
console.log(elements.join('')) // 使用''代替,
console.log(elements.join('-')) // 用 - 拼接
3.forEach方法
forEach() 方法会遍历数组,并且将数组的每一项作为参数传入到回调函数中,无返回值
数组.forEach((数组项:类型,索引:number)=>{
// 逻辑
})
数组.forEach(数组项=>{
// 逻辑
})
-------基于提供的数据,通过 forEach 依次打印每一项的 name和 title 属性
// 接口
interface HeroInfo {
id: number
name: string
title: string
}
// 数组
const arr: HeroInfo[] = [
{ id: 105, name: '廉颇', title: '正义爆轰' },
{ id: 106, name: '小乔', title: '恋之微风' },
{ id: 107, name: '赵云', title: '苍天翔龙' },
{ id: 108, name: '墨子', title: '和平守望' },
]
arr.forEach((item:HeroInfo)=>{
console.log('测试',item.name,item.title)
})
4.map方法
map() 方法创建一个新数组,基于原数组,创建一个新数组(映射)
- 如果能推断出类型,泛型参数可以不写
- 如果无法推断,需要人为指定类型
数组.map<返回值类型>((数组项,索引)=>{ return xxx })
// 如果返回值的类型编译器无法推断,就需要自行设置
---------应用
// map 基本使用01- 基于numArr返回新数组,每一个数组翻倍
// map 基本使用01- 基于numArr返回新数组,每一个数组翻倍,并转为字符串
const numArr: number[] = [1, 2, 3, 4, 5]
const numArr2: number[] = numArr.map((v => {
return v * 2
}))
const strArr: string[] = numArr.map((v => {
return (v * 2).toString()
}))
// map 基本使用03 - 基于 foodArr 返回新数组,每一项 都是一个对象
// name 为食物名,price 为5-10 的随机价格 ,比如{ name:'西兰花',price:10 }
interface FoodInfo {
name: string
price: number
}
const foodArr: string[] = ['西兰花', '西葫芦', '西红柿', '西北风']
foodArr.map<FoodInfo>((v => {
return {
name: v,
price: Math.random() * 11 + 5
}
}))
5.filter方法
根据回调函数的执行结果,筛选出符合要求的元素,返回一个新数组
数组.filter((数组项:类型,索引:number)=>{
return true // 或者可以解析为 true 的值 需要这一项
return false // 或者可以解析为 false 的值 不需要这一项
})
--------应用
// filter基本使用 01 - 筛选出大于 5 的数
const nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
const res1 = nums.filter((item: number, index: number) => {
if (item > 5) {
return true
} else {
return false
}
})
console.log('筛选之后的结果是:', res1)
interface Food {
name: string
price: number
}
// filter基本使用 02 - 筛选出价格大于 5 的食物
const foods: Food[] = [
{
name: '西兰花',
price: 6
},
{
name: '西红柿',
price: 3
},
{
name: '榴莲',
price: 30
},
{
name: '葱花',
price: 1
}
]
const res2 = foods.filter((item: Food, index: number) => {
if (item.price > 5) {
return true
} else {
return false
}
})
// 复杂类型的数据(对象,对象数组),可以通过 JSON.stringify()转为 字符串进行打印
console.log('筛选之后的食物:', JSON.stringify(res2))
6.sort方法
方法就地对数组的元素进行排序,并返回对相同数组的引用。排序是改变原数组
// 自定义排序规则
数组.sort((第一个比较元素,第二个比较元素)=>{
return 大于0 值 // a 会调换到 b 的后面
return 小于0 值 // a 会继续留在 b 的前面
return 0 // 不变
})
------------------------应用
// 数值数组排序
const arr = [5, 12, 8, 130, 44]
arr.sort((a,b)=>{
// return a-b
return b-a
})
console.log(arr.toString())
interface Food{
name:string
price:number
}
// 对象数组排序
const foods:Food[] = [
{
name: '西兰花',
price: 6
},
{
name: '西红柿',
price: 3
},
{
name: '榴莲',
price: 30
},
{
name: '葱花',
price: 1
}
]
foods.sort((a,b)=>{
return a.price-b.price
})
console.log(JSON.stringify(foods))
7.reduce方法
reduce方法的作用是求和,他会逐个遍历数组元素,每一步都将当前元素的值与前一步的结果相加
// 纯数值类数组求和
reduce((上一次累加结果,当前元素值)=>{})
// 对象数组求和
reduce((上一次累加结果,当前元素值)=>{}, 初始值)
// 数值数组排序
const arr = [5, 12, 8, 130, 44]
const res = arr.reduce((a, b) => {
// return a-b
return a + b
})
console.log('res:', res)
interface Food {
name: string
price: number
count: number
}
// 对象数组排序
const foods: Food[] = [
{
name: '西兰花',
price: 6,
count: 2
},
{
name: '西红柿',
price: 3,
count: 3
},
{
name: '榴莲',
price: 30,
count: 2
},
{
name: '葱花',
price: 1,
count: 10
}
]
const total = foods.reduce((a, b) => {
return a + b.price * b.count
}, 0)
console.log('total:', total)
8.findIndex方法
findIndex() 方法返回数组中满足提供的测试函数的第一个元素的索引。若没有找到对应元素则返回 -1
// 索引,如果用不上可以省略不写
数组.findIndex((数组项:类型,索引:number)=>{
return true // 找到了需要的元素
return false // 或者可以解析为 false 的值 不是这一项
})
// 1. 字符串数组
const arr = ['西兰花', '西葫芦', '西兰花']
arr.findIndex((item: string, index: number) => {
if (item == '西葫芦') {
return true
} else {
return false
}
})
9.find方法
find() 方法返回通过测试(函数内判断)的数组的第一个元素的值,找不到返回 undefined
// 索引,如果用不上可以省略不写
数组.find((数组项:类型,索引:number)=>{
return true // 找到了需要的元素
return false // 或者可以解析为 false 的值 不是这一项
})
// 1. 字符串数组
const arr = ['西兰花', '西葫芦', '西兰花']
let idx:string|undefined = arr.find((item: string, index: number) => {
if (item == '西葫芦') {
return true
} else {
return false
}
})
console.log('测试',idx)//测试 西葫芦
10.every方法
every 方法方测试一个数组内的【所有元素】是否都能通过指定函数的测试。它返回一个布尔值
// 索引,如果用不上可以省略不写
const res = 数组.every((数组项:类型,索引:number)=>{
return true // 当前元素符合要求 返回 true
return false // 当前元素不符合要求 返回 false
})
// res 可以获取是否每个元素都符合要求
-----------应用
// 1. 数值数组 判断是否每个元素都大于 2
const numArr:number[] = [2,4,6,8,10]
let res:boolean = numArr.every((item:number)=>{
return item>2
})
console.log('测试',res)
// 2. 字符串数组 判断是否每个元素里面都有绿字
const foodArr:string[] = ['绿茶','绿豆','绿色能源','绿色心情','茉莉奶绿']
let res2:boolean = foodArr.every((item:string)=>{
return item.includes('绿')
})
console.log('测试',res2)
11.some方法
some 方法测试数组中是否【至少有一个元素】通过了由提供的函数实现的测试。
// 索引,如果用不上可以省略不写
const res = 数组.every((数组项:类型,索引:number)=>{
return true // 当前元素符合要求 返回 true
return false // 当前元素不符合要求 返回 false
})
// res 可以获取是否 【至少有一个元素】符合要求
--------应用
// 1. 数值数组 判断是否每个元素都大于 2
const numArr:number[] = [2,4,6,8,10]
let res:boolean = numArr.some((item:number)=>{
return item>2
})
console.log('测试',res)
// 2. 字符串数组 判断是否每个元素里面都有绿字
const foodArr:string[] = ['绿茶','绿豆','绿色能源','绿色心情','茉莉奶绿']
let res2:boolean = foodArr.some((item:string)=>{
return item.includes('张三')
})
console.log('测试',res2)
10.函数补充
1.回调函数
作为实参传入另一个函数,并在该函数内被调用,用来完成某些任务的函数,成为回调函数
咱们用过很多次,比如:
Button.onClick(()=>{})
Scroll(){
}.onScroll(()=>{})
List(){
}.onScrollToIndex(()=>{})
Image()
.onAppear(()=>{});
[1,2,3].filter((item:number,index:number)=>{})
// ...
// 作为参数传递的函数 都是 回调函数
2.箭头函数化简
箭头函数在日常使用中,如果满足一些特定的条件,可以写的更为简洁
简写条件:
-
函数体只有一行,可以省略大括号
-
省略大括号时,如果有 return,也需要一起省略
-
参数只有一个,且不需要写类型,可以省略小括号
const numArr = [1, 2, 3, 4] numArr.forEach((item: number) => { console.log('', item) }) const res = numArr.map((item: number) => { return item * 2 }) const res2 = numArr.filter((item: number) => { if (item > 2) { return true } else { return false } }) -------------------简写形式 const numArr = [1, 2, 3, 4] numArr.forEach(item => console.log('', item)) const res = numArr.map(item => item * 2) const res2 = numArr.filter(item => item > 2)
11.正则表达式
1.概述
正则表达式是用于匹配字符串中字符组合的模式(规则)
日常开发中主要用来做三件事:匹配、替换、提取
- 手机号表单要求用户只能输入11位的数字 (匹配)
- 过滤掉页面内容中的一些敏感词(替换)
- 从字符串中获取我们想要的特定部分(提取)等
2.基本使用
-
定义正则
-
使用正则
// 方式 1:简写 const res1: RegExp = /ArkTS/ // 方式 2:通过实例化的方式创建 const reg2: RegExp = new RegExp('ArkTS') console.log('web:', res1.test('ArkTS')) // true console.log('web:', res1.test('Java')) // false
3.元字符
元字符指的是在正则表达式中,有特殊含义的符号
正则表达式中绝大多数的字符都是描述他们本身,比如:
/ArkTS/ // 表示 ArkTS 这 5 个字母
有一些具有特殊含义的字符,可以极大的提高正则表达式的灵活性和功能,比如:
/[a-z]/ // 只能是 a-z中的字母
像上面的 [] , - 就是元字符,接下来咱们来看看有哪些常用的元字符
1.边界符
正则表达式中的边界符(位置符)用来提示字符所处的位置,主要有两个字符
| 边界符 | 说明 |
|---|---|
| ^ | 表示匹配行首的文本(以谁开始) |
| $ | 表示匹配行尾的文本(以谁结束) |
如果 ^ 和 $ 在一起,表示必须是精确匹配
// 元字符之边界符
// 1. 匹配开头的位置 ^
const reg = /^ArkTS/
console.log('res:', reg.test('ArkTS语法')) //true
console.log('res:', reg.test('学习ArkTS')) //false
console.log('res:', reg.test('学习ArkTS语法')) //false
console.log('res:', reg.test('Ar')) //false
// 2. 匹配结束的位置 $
const reg1 = /ArkTS$/
console.log('res1:', reg1.test('ArkTS语法')) //false
console.log('res1:', reg1.test('学习ArkTS')) //true
console.log('res1:', reg1.test('学习ArkTS语法')) //false
console.log('res1:', reg1.test('Ar')) //false
// 3. 精确匹配 ^ $
const reg2 = /^ArkTS$/
console.log('res2:', reg2.test('ArkTS语法')) //false
console.log('res2:', reg2.test('学习ArkTS')) //false
console.log('res2:', reg2.test('学习ArkTS语法')) //false
console.log('res2:', reg2.test('ArkTS')) //true
console.log('res2:', reg2.test('Ar')) //false
console.log('res2:', reg2.test('ArkTSArkTS')) //false
2.量词
量词用来设定某个模式的重复次数
| 量词 | 说明 |
|---|---|
| * | 重复零次或更多次 |
| + | 重复一次或更多次 |
| ? | 重复零次或一次 |
| {n} | 重复 n 次 |
| {n,} | 重复 n 次或更多次 |
| {n,m} | 重复 n 到 m 次 |
// 元字符之量词
// 1. * 重复次数 >= 0 次
const reg1 = /^w*$/
console.log('res:', reg1.test('')) //true
console.log('res:', reg1.test('w')) //true
console.log('res:', reg1.test('ww')) //true
console.log('res:', '-----------------------')
// 2. + 重复次数 >= 1 次
const reg2 = /^w+$/
console.log('res:', reg2.test('')) //false
console.log('res:', reg2.test('w')) //true
console.log('res:', reg2.test('ww')) //true
console.log('res:', '-----------------------')
// 3. ? 重复次数 0 || 1
const reg3 = /^w?$/
console.log('res:', reg3.test('')) //true
console.log('res:', reg3.test('w')) //true
console.log('res:', reg3.test('ww')) //false
console.log('res:', '-----------------------')
// 4. {n} 重复 n 次
const reg4 = /^w{3}$/
console.log('res:', reg4.test('')) //false
console.log('res:', reg4.test('w')) //false
console.log('res:', reg4.test('ww')) //false
console.log('res:', reg4.test('www')) //true
console.log('res:', reg4.test('wwww')) //false
console.log('res:', '-----------------------')
// 5. {n,} 重复次数 >= n
const reg5 = /^w{2,}$/
console.log('res:', reg5.test('')) //false
console.log('res:', reg5.test('w')) //false
console.log('res:', reg5.test('ww')) //true
console.log('res:', reg5.test('www')) //true
console.log('res:', '-----------------------')
// 6. {n,m} n =< 重复次数 <= m
const reg6 = /^w{2,4}$/
console.log('res:', reg6.test('w')) //false
console.log('res:', reg6.test('ww')) //true
console.log('res:', reg6.test('www')) //true
console.log('res:', reg6.test('wwww')) //true
console.log('res:', reg6.test('wwwww')) //false
3.范围
表示字符的范围,定义的规则限定在某个范围,比如只能是英文字母,或者数字等等,用表示范围
| 范围 | 说明 |
|---|---|
| [abc] | 匹配包含的单个字符。也就是只有a ll b ll c 这三个单字符返回true,可以理解为多选1 |
| [a-z] | 连字符。来指定字符范围。[a-z]表示 a到226个英文字母 |
| [^abc] | 取反符。[^a-z]匹配除了小写字母以外的字符 |
// 元字符之范围 []
// 1. [abc] 匹配包含的单个字符, 多选1
const reg1 = /^[abc]$/
console.log('res:', reg1.test('a')) //true
console.log('res:', reg1.test('b')) //true
console.log('res:', reg1.test('c')) //true
console.log('res:', reg1.test('d')) //false
console.log('res:', reg1.test('ab')) // false
console.log('测试','--------------------------')
// 2. [a-z] 连字符 单个
const reg2 = /^[a-z]$/
console.log('res:', reg2.test('a')) //true
console.log('res:', reg2.test('p')) //true
console.log('res:', reg2.test('0')) //false
console.log('res:', reg2.test('A')) //false
console.log('测试','-------------------------')
// 3. [^a-z] 取反符
const reg5 = /^[^a-z]$/
console.log('res:', reg5.test('a')) //false
console.log('res:', reg5.test('A')) //true
console.log('res:', reg5.test('么')) //true
console.log('测试','-------------------------')
// 4.想要包含小写字母,大写字母 ,数字
const reg3 = /^[a-zA-Z0-9]$/
console.log('res:', reg3.test('B')) //true
console.log('res:', reg3.test('b')) //true
console.log('res:', reg3.test('9')) //true
console.log('res:', reg3.test(',')) //false
console.log('测试','-------------------------')
// 5.试一试:用户名可以输入英文字母,数字,可以加下划线,要求 6~16位
const reg4:RegExp =/^[a-zA-Z0-9_]{6,12}$/
console.log('测试',reg4.test('jack_1321'))//true
console.log('测试',reg4.test('1321'))//false
4.字符类
某些常见模式的简写方式,区分字母和数字
| 字符类 | 说明 |
|---|---|
| \d | 匹配0-9之间的任一数字,相当于[0-9] |
| \D | 匹配所有0-9以外的字符,相当于[^0-9] |
| \w | 匹配任意的字母、数字和下划线,相当于[A-Za-z0-9_] |
| \W | 除所有字母、数字和下划线以外的字符,相当于[^A-Za-z0-9_] |
| \s | 匹配空格(包括換行符、制表符、空格符等),相等于[\t\r\n\v\f] |
| \S | 匹配非空格的字符,相当于[^\t\r\n\v\f] |
// 试一试。匹配 1990-10-10 、1990-1-1 这种格式
const reg:RegExp =/^\d{4}-\d{1,2}-\d{1,2}$/
console.log('测试',reg.test('1990'))
console.log('测试',reg.test('1990-1-1'))
console.log('测试','---------------------------')
//完整版日期正则
const reg1:RegExp = /^(19|20)\d{2}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/
console.log('测试',reg1.test("1990-10-10")); // true
console.log('测试',reg1.test("1990-1-1")); // false
console.log('测试',reg1.test("1990-13-01")); // false
console.log('测试',reg1.test("1990-01-32")); // false
console.log('测试',reg1.test("2000-02-29")); // true,这是简单版不能判断闰年
console.log('测试',reg1.test("2004-02-29")); // true,因为2004年是闰年
5.替换和修饰符
字符串的 replace 方法,可以结合正则进行字符串的替换
默认是不是只能检索一个?
使用正则结合修饰符可以实现大小写区分,是否匹配所有
| 修饰符 | 说明 |
|---|---|
| i | 单词 ignore 的缩写,正则匹配时字母不区分大小写 |
| g | 单词 global 的缩写,匹配所有满足正则表达式的结果 |
// 检索规则:文本、正则
// 替换内容:文本
// 返回值:替换之后的结果
字符串.replace(检索规则,替换内容)
// 替换和修饰符
const str = '欢迎大家学习ArkTS,相信大家一定能学好ArkTS!!!'
// 将 ArkTS 替换位 鸿蒙开发咋写? 分别用正则和字符串作为检索规则
--------------试一试
// 替换和修饰符
const str = '欢迎大家学习ArkTS,相信大家一定能学好ArkTS!!!'
// replace 返回值是替换完毕的字符串
console.log(str.replace(/arkts/, '鸿蒙开发')) //欢迎大家学习ArkTS,相信大家一定能学好ArkTS!!!
console.log(str.replace(/arkts/i, '鸿蒙开发')) // 欢迎大家学习鸿蒙开发,相信大家一定能学好ArkTS!!!
console.log(str.replace(/arkts/gi, '鸿蒙开发')) //欢迎大家学习鸿蒙开发,相信大家一定能学好鸿蒙开发!!!
console.log(str.replace(/arkts/ig, '鸿蒙开发')) //欢迎大家学习鸿蒙开发,相信大家一定能学好鸿蒙开发!!!