TypeScript语言进阶
Bigint
BigInt是在2019年9月被正式纳入ECMAScript标准中的特性。虽然BigInt不是频繁使用的特性, 但其特殊性在于它是一种新的原始数据类型,同时又属于数值类型的一种。由于BigInt类型的加入, JavaScript中共支持两种数值类型,即Number类型和BigInt类型。目前,JavaScript语言中共有以下七种原始数据类型:
- Undefined
- Null
- Strng
- Boolean
- Number
- Symbal
- Nigint
JavaScript语言使用双精度64位浮点数格式来表示Number类型的值。Number类型能够安全表示的最大整数为2^53^-1,该数值能够使用内置的Number对象上的MAX_SAFE_INTEGER属性来表示。BigInt类型能够表示任意精度的整数,尤其是大于2^53^-1的整数,这也正是引入BigInt类型的原因。
创建Bigint
我们可以使用以下两种方式来创建BigInt类型的值:
使用BigInt字面量。
使用BigInt()函数。
BigInt字面量的语法是在一个整数后面添加一个小写字母“n”。字母“n”必须紧随数字之后,两者之间不允许存在空白字符。示例如下:
const unit0 = 1n; // Bigint字面量
const unit1 = BigInt(1) // BigInt()函数 1n
BigInt 与Number
BigInt类型的值能够与Number类型的值进行大小及相等关系的比较。在进行严格相等===比较时, BigInt类型的值与Number类型的值永远不相等。在进行非严格相等==比较及大小关系比较时,BigInt 类型的值与Number类型的值将进行数学意义上的比较。
虽然BigInt类型的值可以与Number类型的值进行比较,但是BigInt类型的值不允许与Number类型的值一起进行混合数学运算。示例如下:
1+1n //类型错误!无法混合使用BigInt和其他类型
通过内置的Number()函数能够将BigInt类型的值转换为Number类型的值。但要注意,在BigInt 类型与Number类型之间进行强制类型转换时有可能损失精度。示例如下:
Number(1n); // 1
展开运算符
展开运算符是ECMAScript 2015中定义的运算符,可以用在多种上下文中,比如对象字面量、 数组字面量和函数调用语句等。展开运算符使用连续的三个点符号“...”来表示。展开运算符的后面是一个表达式,表达式的求值结果为要展开的值。
展开数组字面量
开数组字面量(Spread Syntax)来展开一个数组,将其元素逐个插入到另一个数组中。展开数组字面量使用三个连续的点(...)作为操作符。
下面是展开数组字面量的示例:
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5, 6];
console.log(arr2); // 输出: [1, 2, 3, 4, 5, 6]
在上述示例中,...arr1
表示展开数组 arr1
,将其元素逐个插入到新的数组 arr2
中。
展开对象字面量
展开对象字面量(Spread Syntax)来展开一个对象,将其属性逐个插入到另一个对象中。展开对象字面量使用三个连续的点(...)作为操作符。
下面是展开对象字面量的示例:
const obj1 = { foo: 1, bar: 2 };
const obj2 = { ...obj1, baz: 3 };
console.log(obj2); // 输出: { foo: 1, bar: 2, baz: 3 }
在上述示例中,...obj1
表示展开对象 obj1
,将其属性逐个插入到新的对象 obj2
中。
展开函数参数
展开函数参数(Spread Syntax)来传递一个数组或对象作为函数的参数,并将其展开为单独的参数。
如果要展开数组作为函数参数,可以使用展开操作符(...)在函数调用时将数组展开为单独的参数。示例如下:
function myFunction(a: number, b: number, c: number) {
console.log(a, b, c);
}
const arr = [1, 2, 3];
myFunction(...arr); // 等同于 myFunction(1, 2, 3)
在上述示例中,使用展开操作符 ...
将数组 arr
展开为单独的参数,从而调用 myFunction
函数并传递参数 1, 2, 3
。
如果要展开对象作为函数参数,可以使用展开操作符(...)在函数调用时将对象的属性展开为单独的参数。示例如下:
function myFunction(a: number, b: number, c: number) {
console.log(a, b, c);
}
const obj = { a: 1, b: 2, c: 3 };
myFunction(...Object.values(obj)); // 等同于 myFunction(1, 2, 3)
在上述示例中,使用 Object.values()
方法获取对象 obj
的所有属性值,然后使用展开操作符 ...
将这些属性值展开为单独的参数,从而调用 myFunction
函数并传递参数 1, 2, 3
。
展开函数参数可以方便地传递数组或对象的值作为函数参数,使代码更简洁易读。需要注意的是,展开函数参数只能用于函数调用,不能用于函数定义。
解构
JavaScript中的解构是指将数组或对象在结构上进行分解,将其拆分成独立的子结构,然后可以访问那些拆分后的子结构。解构提供了一种更加便利的数据访问方式,使用解构语法能够极大地简化代码。
数组解构
数组解构(Array Destructuring)语法从数组中提取值并将其赋给变量。
数组解构赋值使用了类似于数组字面量的表示方式。赋值运算符右侧为需要解构的数组,赋值运算符左侧是解构赋值的目标,在解构赋值的同时也支持声明新的变量。
数组解构使用方括号([])来指定要解构的数组,并使用等号(=)将解构的值赋给变量。示例如下:
const arr = [1, 2, 3];
// 解构数组
const [a, b, c] = arr;
console.log(a); // 输出 1
console.log(b); // 输出 2
console.log(c); // 输出 3
在上述示例中,通过将数组 [1, 2, 3]
解构为 [a, b, c]
,可以将数组中的元素分别赋给变量 a
、b
和 c
。
对象解构
象解构(Object Destructuring)语法从对象中提取值并将其赋给变量。
对象解构使用花括号({})来指定要解构的对象,并使用等号(=)将解构的值赋给变量。示例如下:
const obj = { x: 1, y: 2, z: 3 };
// 解构对象
const { x, y, z } = obj;
console.log(x); // 输出 1
console.log(y); // 输出 2
console.log(z); // 输出 3
在上述示例中,通过将对象 { x: 1, y: 2, z: 3 }
解构为 { x, y, z }
,可以将对象中的属性值分别赋给变量 x
、y
和 z
。
可选链运算符
可选链运算符是2019年12月纳入ECMAScript标准中的新特性。TypeScript 3.7版本增加了对可选链运算符的支持,因此我们可以在TypeScript 3.7及以上的版本中直接使用该运算符。
当尝试访问对象属性时,如果对象的值为undefined或null,那么属性访问将产生错误。为了提高程序的健壮性,在访问对象属性时通常需要检查对象是否已经初始化,只有当对象不为undefined 和null时才去访问对象的属性。可选链运算符旨在帮助开发者省去冗长的undefined值和null值检查代码,增强了代码的表达能力。
可选链运算符由一个问号和一个点号组成,即“ ?. ”。可选链运算符有以下三种语法形式:
可选的静态属性访问。
可选的计算属性访问。
可选的函数调用或方法调用。
可选的静态属性访问
const obj = {
prop1: {
prop2: {
prop3: 'value'
}
}
};
console.log(obj?.prop1?.prop2?.prop3); // 输出 'value'
console.log(obj?.prop1?.prop2?.prop4); // 输出 undefined
在该语法中,如果obj的值为undefined或null,那么表达式的求值结果为undefined;否则,表达式的求值结果为obj.prop
可选的计算属性访问
obj?.[expr]
在该语法中,如果obj的值为undefined或null,那么表达式的求值结果为undefined;否则, 表达式的求值结果为obj[expr]。
可选的函数调用或方法调用
const obj2: MyObject = {};
obj2.func1?.(); // 不会调用任何函数,没有输出
obj2.obj1?.func2?.(); // 不会调用任何函数,没有输出
通过使用可选链运算符 ?.
,可以安全地调用函数 func1
和 func2
,即使它们可能不存在或为 undefined
。如果函数存在,则会执行相应的函数调用操作,否则整个调用表达式的结果为 undefined
。
短路求值
如果可选链运算符左侧操作数的求值结果为undefined或null,那么右侧的操作数不会再被求值,我们将这种行为称作短路求值。在下例中,由于变量a的值为undefined,因此第4行中的变量x 将不会执行自增运算:
let x= 0;
let a = undefined;
a?.[++x];//undefined X; 0
值得一提的是,二元逻辑运算符“&&”和“|”也具有短路求值的特性。
空值合并运算符
空值合并运算符在2019年11月成为ECMAScript标准中的候选特性。虽然还不是最终的标准, 但核心功能已经基本确定。TypeScript 3.7版本增加了对空值合并运算符的支持,因此我们可以在 TypeScript 3.7以上的版本中直接使用该运算符。
空值合并运算符与可选链运算符一样都具有短路求值的特性。当空值合并运算符左侧操作数的值不为undefined和null时,右侧操作数不会被求值,而是直接返回左侧操作数。
空值合并运算符与逻辑或运算符 ||
有些相似,但在处理空值时有区别。逻辑或运算符会对左侧的操作数进行强制类型转换,将类似 false
、0
、""
等 Falsy 值也视为假,因此如果左侧的操作数为 Falsy 值时,会返回右侧的操作数。而空值合并运算符只会对 null
和 undefined
进行判断,其他 Falsy 值仍然会被视为真。
下面是使用空值合并运算符的示例:
const foo = null ?? 'default'; // foo 的值为 'default',因为左侧的操作数为 null
const bar = undefined ?? 'default'; // bar 的值为 'default',因为左侧的操作数为 undefined
const baz = 0 ?? 42; // baz 的值为 0,因为左侧的操作数不为 null 或 undefined
const qux = false ?? true; // qux 的值为 false,因为左侧的操作数不为 null 或 undefined
空值合并运算符可以用于给变量提供默认值,当变量为 null
或 undefined
时,可以使用空值合并运算符提供一个备选的默认值。这在处理函数参数、对象属性等情况下特别有用。
function greet(name?: string) {
const userName = name ?? 'Anonymous';
console.log(`Hello, ${userName}!`);
}
greet(); // 输出 "Hello, Anonymous!"
greet('Alice'); // 输出 "Hello, Alice!"
Falsy 值是指在布尔上下文中被视为假(false
)的值。在 JavaScript 和 TypeScript 中,以下值被视为 Falsy 值:
false
:布尔类型的false
0
:数值类型的零''
:空字符串null
:表示空值或无效对象的特殊关键字undefined
:表示未定义的值NaN
:表示非数值的特殊值
这些值在条件判断、逻辑运算和类型转换中被视为假,当它们作为条件表达式的结果时,会被自动转换为 false
。其他非 Falsy 值都被视为真(true
)。