1、ArkTS常见的数据类型有哪些?
①为什么要有数据类型
例子:在烹饪中,我们需要知道食材的种类(如蔬菜、肉类等)和数量(如1个苹果、200克鸡肉等)。如果我们不区分这些食材的种类和数量,那么在做菜时就会遇到问题。例如,如 果我们将所有食材都视为“食品”,而不去区分它们的具体类型和数量,那么我们就无法正确地处理它们,可能做出的食物就不是我们想要的结果。同样,在编程中,我们也需要使用数据类型来区分不同的数据。例如,我们需要知道一个变量是一个数字还是一个字符串,这样我们才能正确地处理它。如果我们不使用数据类型,那么我们就无法确保程序的正确性,可能会导致程序出错或者运行结果不符合预期。
答案:为了程序员更好的操作数据,对数据进行了分类
②分类
例子:
1. 基本数据类型:我们可以将它们与一些基本的物理量对应起来。例如,数字可以代表长度、重量、时间等;字符串可以代表姓名、地址、电话号码等;布尔值可以代表真假、开闭等。
2. 复合数据类型:数组可以代表一组人、一组物品等;映射可以代表一种关系,如字典中的单词与解释之间的关系;集合可以代表一组无序且不重复的元素,如购物车中的商品列表。
3. 对象类型:一个人可以用一个对象来表示,其中包含了该人的姓名、年龄、性别等属性和吃饭、睡觉等方法。
4. 函数类型:在实际生活中,我们可以将函数与一些操作或计算对应起来。例如,计算两个数的和可以用一个函数来实现。
5. 高级类型:我们可以将这些高级类型与一些复杂的数据结构或行为对应起来。例如,泛型可以用来表示一个容器可以存放任何类型的元素;类型别名可以为现有的数据类型定义一个新的名字;元组可以表示一个已知元素数量和类型的数组;枚举可以表示一组有名称的常量;any类型可以表示任意类型的数据;void类型可以表示没有任何类型的数据。
答案:基本数据类型、复合数据类型、对象类型、函数类型、高级类型
③详细分类
例子:同上
答案:
基本数据类型:number:表示数字,包括整数和浮点数;string:表示文本字符串;boolean:表示布尔值,即true或false;null、undefined:分别表示null和undefined;symbol:表示唯一的、不可变的值。
复合数据类型:array:表示数组,可以使用number[]或者Array<number>来声明其中元素的类型;tuple:表示元组,用于表示固定数量和类型的数组;enum:表示枚举类型,用于定义常量集合。
对象类型:object:表示非原始数据,即除number、string、boolean、symbol、null或undefined之外的类型; interface:用于描述对象的结构,并且可以重复使用。
函数类型:function:表示函数类型;void:表示函数没有返回值;any表示任意类型。
高级类型:union types:表示一个值可以是几种类型之一;intersection types:表示一个值同时拥有多种类型的特性。
tuple:
let tupleArr:[number,string,boolean];
tupleArr = [12,'34',true]; //ok
tupleArr = [12,'34'] // no ok
never:
let a:never;
a = 123; //错误的写法
a = (()=>{
//正确的写法
throw new Error('错误');
})()
//返回never的函数必须存在无法发达的终点
function error(message:string):never{
throw new Error(message);
}
2、typeof 返回的数据类型?
①是什么
例子:如果你有一个变量是用来存储水果的价格,那么这个变量的数据类型可能是数字(number)。如果你有一个变量是用来存储顾客的名字,那么这个变量的数据类型可能是字符串(string)。typeof就是用来查看这个"标签"的工具。你可以使用typeof来检查你的变量是否是正确的数据类型,以确保你的程序能够正确地运行。
答案:typeof用于检测数据的类型,一般用于检测简单数据类型
②返回结果
例子:返回结果就像是超市商品标签上的信息,告诉你每个变量是什么类型的"商品"。
答案:'number' 'string' 'boolean' 'undefined' 'object' 'function'
③特殊情况
答案: typeof null 返回 'object', typeof array 返回 'object'
3、转布尔值返回false的情况有哪些?
答案:常见情况:undefined、0、 ""、null、false、NaN、不成立的表达式
4、any类型的作用是什么?
例子:有时你可能需要一个变量来存储不同类型的数据。例如,你可能需要一个变量来存储用户输入的信息,而用户输入的信息可能是数字、字符串、布尔值等等。在这种情况下,你可以使用any类型来定义这个变量,这样你就可以将任何类型的值赋给它。
答案:any类型的作用是允许我们在编写代码时不指定具体的类型,从而可以接受任何类型的值。使用any类型相当于放弃了对该值的静态类型检查,使得代码在编译阶段不会对这些值进行类型检查。主要情况下,any类型的使用包括以下几点:
当我们不确定一个变量或表达式的具体类型时,可以使用any类型来暂时绕过类型检查。
在需要与动态类型的后端代码交互时,可以使用any类型来处理这些动态类型的值。
有时候某些操作难以明确地定义其类型,或者需要较复杂的类型推导时,也可以使用any类型。
滥用的后果:尽管any类型提供了灵活性,但由于它会放弃TypeScript的静态类型检查,因此滥用any类型可能会降低代码的健壮性和可维护性。当滥用any类型时,可能会导致以下结果:
代码可读性下降:
let data:any;
//代码中的使用方式
data.someUnknownMethod();//在编译阶段不会报错,但实际上可能是一个错误
潜在的运行时错误:
let myVariable:any = 123;
myVariable.toUpperCase(); //在编译阶段不会报错,但在运行时会引发错误
类型安全受损:
function add(x:any,y:any):any{
return x+y; //编译器无法推断返回值的具体类型
}
滥用any类型会导致代码失去了TypeScript强大的类型检查功能,带来了如下问题:
可能引入未知的运行时行为和错误。
降低了代码的可维护性和可读性,因为难以理解某些变量或参数的具体类型
5、==和===的区别?
例子:想象一下你在超市购物的情景。当你拿起一个苹果和一个橙子的时候,你知道它们的数量都是1,这里相等的是数量,用的是等于(==),如果再加上种类的话,用的就是全等(===),此情景不全等
①是什么
答案:都是我们的比较运算符,用来判断数据相等还是全等
②区别
答案:==表示是相等,比较值是否相等,===表示是全等,不仅比较值,也比较类型是否相等
6、null和undefined的区别?
例子:想象一下你在超市购物的情景。当你问两个商家有没有西瓜的时候,第一个商家说卖完了,就是说现在是空的,这里就是空值(null);当你问另外一个商家有没有的时候,第二个商家说我家根本就不卖西瓜,也就是说根本就没有进过这个货,这里就是还未定义(undefined)
①是什么?
答案:都是属于数据类型的基本数据类型。
②区别?
答案:null表示空值 typeof null 返回“object”,返回null的情况:没有回去到元素,没有获取到本地存储数据;
undefined表示未定义,typeof udefined 返回“undefined”,返回undefined的情况:声明变量没有赋值
7、let、const和@State的区别?
①是什么?
答案:都是用来声明变量的关键字
②区别?
答案:let和const的特点:不能在定义前使用;指定的类型和存储的类型要一致;不能重复定义。
const和@State的特点:定义必须要赋值;let/const值改变不能在UI中更新;@State值改变,可以在UI中更新。
8、值类型和引用类型的区别?
①是什么?
答案:ArkTS中数据分为值类型(基本数据类型)和引用类型(复杂数据类型)
②区别?
答案:存储位置不一样:值类型的会保存到栈内存;引用类型的变量名(地址)会存在栈内存中,但是值会存储在堆内存中
赋值方式不一样:值类型的直接赋值,赋的值本身;引用类型赋值,赋的是地址(影响=>修改值会相互影响=>解决:浅拷贝或深拷贝)
9、0.1加0.2是否等于0.3?原因和解决办法
①是什么?
答案:浮点数精度丢失问题,0.1+0.2不等于0.3
②原因?
答案:0.1和0.2在二进制中却是一个表现不出来的无限不循环数,所以只能取一个近似数。而计算机精度有限,所能表现的值而非真正的0.1,0.2,所以自然相加时有偏差
③解决
答案:我们可以先将其转化为整数,运算之后,然后再将其转化为小数
方法:filter加includes
10、数组的方法和作用?
①是什么?
答案:数组是有序的数据的集合
②方法?
答案:定义数组:let 数组名:类型[] = [数据1,数据2,...]
数组取值:数组名[下标]
数组添加内容:数组.push(数据1,...)向数组最后添加;数组.unshift(数据1,...)向数组最前添加
数组删除内容:数组.pop()删除数组的最后一个;数组.shift()删除数组的第一个
指定删除数组内容:数组.splice(下标,个数)
11、函数相关内容
①是什么?
答案:用于封装可以重复执行的代码块,帮助我们实现代码的复用和封装
②使用方式?
答案:可以通过function关键字、箭头函数等形式去定义,例如下面一个简单的加法函数
const add = (a:number,b:number)=>a+b
当我们没有提供函数实现的情况下,有两种声明函数类型的方式,如下所示:
//方式一
type LongHand = {
(a:number):number
}
//方式二
type ShortHand = (a:number)=>number
③函数参数
答案:函数调用传递的参数是-实参;函数定义写的参数是-形参
④可选参数
答案:当函数的参数可能是不存在的,只需要在参数后面加上?代表参数可能不存在,如下:
const add = (a:number,b?:number)=>a+(b?b:0)
⑤剩余参数
答案:剩余参数与JavaScript的语法类似,需要用...来表示剩余参数,如果剩余参数rest是一个由number类型组成的数组,则如下表示:
const add = (a:number,...rest:number[])=>rest.reduce(((a,b)=>a+b),a)
12、说一说数组常用的API
答案:数组都是Array的实例化对象,它提供了很多的方法来帮助我们快速处理数据
push():在数组末尾添加一个或多个元素,并返回新的长度。
pop():删除数组的最后一个元素,并返回那个元素。
shift():删除数组的第一个元素,并返回那个元素。
unshift():在数组的开始添加一个或多个元素,并返回新的长度。
slice():返回数组的一个浅拷贝。
splice():通过删除现有元素或添加新元素来更改一个数组内容。
concat():连接两个或多个数组,并返回一个数组的内容。
join():将数组中的所有元素转换为一个字符串。
reverse():颠倒数组中元素的顺序。
sort():对数组的元素进行排序。
forEach():遍历数组中的每个元素并执行回调函数
map():创建一个新数组,其结果是该数组中的每个元素是调用一次提供的函数后的返回值
filter():创建一个新数组,其包含通过所提供函数实现的测试的所有元素。
reduce():对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值。
reduceRight():对数组中的每个元素执行一个由您提供的reducer函数(降序执行),将其结果汇总为单个返回值。
13、new操作符做了什么
答案:创建了一个新对象;函数内部的this指向这个对象;执行构造函数代码;返回新对象
14、说说你对泛型的理解
①是什么
答案:泛型程序设计是程序设计语言的一种风格或范式,定义函数,接口或者类的时候,不预先定义好具体的类型,而在使用的时候在指定类型的一种特性。
②使用方式
答案:泛型通过<>的形式进行表述,可以声明:
函数:
funciton returnItem<T>(para:T):T{
return para
}
接口:
interface ReturnItem<T>{
(para:T):T
}
类:
class Stack<T>{
private:arr:T[] =[]
public push(item:T){
this.arr.push(item)
}
public pop(){
this.arr.pop()
}
}
15、说一下interface和type的区别
答案:①相同点:都可以描述‘对象’或者‘函数’
//interface
interface User{
name:string
age:number
}
interface SetUser{
(name:string,age:numebr):void
}
//type
type User={
name:string
age:number
}
type setUer = (name:string,age:number)=>void
都允许拓展(extends):interface和type都可以拓展,并且两者并不是相互独立的,也就是说interface可以extends type,type也可以extends interface。虽然效果差不多,但是两者语法不同。
②不同点:type可以而interface不行:type可以声明基本类型,联合类型,元组,可以使用typeof获取实例的类型进行赋值
interface可以而type不行:interface能够声明合并:一般来说,如果不清楚什么时候interface/type,能用interface实现,就用interface,如果不能就用type。接口和类型别名的区别:接口定义了一个契约,描述了对象的形状(属性和方法),以便在多个地方共享。它可以被类、对象和函数实现;类型别名给一个类型起了一个新名字,便于在多处使用。它可以用于原始值、联合类型、交叉类型等。与接口不同,类型别名可以用于原始类型、联合类型、交叉类型等,而且还可以为任何类型指定名字
16、枚举(enum)是什么,它的优势,应用案例。枚举和常量枚举的区别?
答案:枚举是一种对数字值集合进行命名的方式。它们可以增加代码的可读性,并提供一种便捷的方式来使用一组有意义的常量。例如:
enum Color{
Red,
Green,
Blue
}
let selectedColor:Color = Color.Red
枚举和常量枚举的区别:枚举可以包含计算得出的值,而常量枚举则在编译阶段被删除,并且不能包含计算得出的值,他只能包含常量成员;常量枚举在编译后会被删除,而普通枚举会生成真实的对象。
17、什么是联合类型和交叉类型?
答案:联合类型表示一个值可以是多种类型中的一种,而交叉类型表示一个新类型,它包含了多个类型的特性。
//联合类型示例:
let myvar:string|number
myvar = "hello" //合法
myvar = 123 //合法
//交叉类型示例:
interface A {
a():void
}
interface B{
b():void
}
type C = A&B //表示同时具备A和B的特性
18、什么是类型断言(Type Assertion)
答案:类型断言允许程序员手动指定一个值的类型。这在需要明确告诉编译器某个值的类型时非常有用。
let someValue:any = "this is a string"
let strLength:number = (someValue as string).length
19、const和readonly的区别?
答案:当在ArkTS中使用const和readonly时,它们的行为有一些显著的区别:
const:const用于声明常量值。一旦被赋值后,其值不能被重新赋值或修改;常量必须在声明时就被赋值,并且该值不可改变;常量通常用于存储不会发生改变的值,例如数字常数或固定的配置值。
const PI = 3.14
PI = 3.14159 //Error:无法重新分配常量
readonly:readonly关键字用于标记类的属性,表明该属性只能在类的构造函数或声明时被赋值,并且不能再次被修改;readonly属性可以在声明时或构造函数中被赋值,但之后不能再被修改;readonly属性通常用于表示对象的某些属性时只读的,防止外部代码修改这些属性的值。
class Person{
readonly name:string
constructor(name:string){
this.name = name //可以在构造函数中赋值
}
}
let person = new Person("Alice")
person.name = "Bob" //Error:无法分配到"name",因为它是只读属性
总结来说,const主要用于声明常量值,而readonly则用于标记类的属性使其只读。
20、interface可以给Function/Array/Class做声明吗?
答案:interface可以用来声明函数、数组和类(具有索引签名的类)。下面是一些示例代码:
//Interface声明函数
interface MyFunc{
(x:number,y:number):number
}
let myAdd:MyFunc = function(x,y){
return x+y
}
//Interface声明数组
interface StringArray{
[index:number]:string
}
let myArray:StringArray
myArray = ["Bob","Alice"]
//Interface声明类(Indexable)
interface StringDictionary{
[key:string]:string
}
let myDict:StringDictionary = {
"name":"John",
"age":"30"
}
综上:interface可以被用来声明函数、数组和具有索引签名的类,从而帮助我们定义和限定这些数据结构的形式和行为。