数据类型
基本数据类型
Undefined type
- 表示未定义值;
- 表示变量声明, 但未初始化的状态;
- 用于可选函数;
Null type
- 表示已经定义, 但是为空值;
- 通常用于初始化对象, 表示一个空的对象;
Boolean type
布尔类型
- true + false;
布尔类型与数字的关系
- true 不等于 1;
- false 不等于 0;
- 两者通过 Number() 可转换为 1 和 0;
任意类型的布尔值
- 任意类型都具有布尔值;
- 可通过 Boolean() 函数转换;
| 数据类型 | 真值 | 假值 |
|---|---|---|
| Boolean | true | false |
| String | 任何非空字符串 | 空字符串 |
| Number | 任何非 0 数字 | 0, NaN |
| Object | 任意对象 | null |
| Undefined | n/a | undefined |
Number Type
基础
格式
- 使用 IEEE–754 表示整数和浮点数;
- 64 位双浮点数精度;
进制
// 十进制
let intNum = 55; // integer
// 二进制
let bNum = 0b10;
// 八进制
let octalNum = 070; // octal for 56
// 十六进制
let hexNum = 0xa;
浮点数
语法格式
let floatNum1 = 1.1;
let floatNum = 3.125e7; // equal to 31250000
let floatNum = 3e-7; // equal to 0.0000003
float 到 int 的隐式转换
- 若小数点后无数字或为 0, 将其转换为 int;
let floatNum1 = 1; // 等效于 int 1
let floatNum2 = 10.0; // 等效于 int 10
精度损失
let a = 0.1 + 0.2; // equal to 0.30000000000000004
数字的范围
常量
- Number.MIN_VALUE: 5e–324;
- Number.MAX_VALUE: 1.7976931348623157e+308;
- Number.MAX_SAFE_INTEGER: 9007199254740991;
- Number.MIN_SAFE_INTEGER: -9007199254740991;
- Number.NEGATIVE_INFINITY: -Infinity;
- Number.POSITIVE_INFINITY: Infinity;
- Number.EPSILON: 2.220446049250313e-16;
- Number.NaN: NaN;
计算机制
- 若计算范围超过 JavaScript 范围;
- 结果为 Infinity/-Infinity;
整数和浮点数的内存耗费
- 数值为整数, 存储在栈上, 耗费 8 字节;
- 数值为浮点数, 存储在堆上, 栈存储堆上的内存地址, 耗费 16 字节;
NaN
NaN
- 表明无效值;
计算特性
- NaN 与任何值计算结果皆为 NaN;
- NaN 不等于任何值, 包括 NaN;
- 只能通过 isNaN() 方法判断是否为 NaN, 其余方法一直为 false;
console.log(typeof NaN); // 输出 "number"
let x = NaN;
console.log(x == NaN); // 输出 "false"
console.log(x === NaN); // 输出 "false"
console.log(isNaN(x)); // 输出 "true"
BigInt
- 任意精度格式的整数;
const bigint = 1234567890123456789012345678901234567890n;
const sameBigint = BigInt("1234567890123456789012345678901234567890");
The String Type
基本概念
string
- 16-bit Unicode;
- immutable;
- 字符序列;
转义字符
| 转义字符 | 意义 | 转义字符 | 意义 |
|---|---|---|---|
| \0 | Null Byte | \' | 单引号 |
| \b | 退格 | \" | 双引号 |
| \n | 换行符 | \ | \ |
| \f | 换页符 | \XXX | 八进制 |
| \r | 回车键 | \xnn | 二位十六进制 |
| \t | 水平制表符 | \unnnn | 四位十六进制 |
length 属性
- 字符串长度;
- 当字符串中包含双字节字符 (如汉字, emoji), length 不一定准确;
模板字面量
基本语法
let name = "kxh";
console.log(`My name is ${name}.`); // My name is kxh;
标签函数
- 若 Template Literal 具有 n 个 Interpolation;
- 第一个参数以 Template Literal 中 n 个 Interpolation 作为分隔点, 构成具有 n + 1 个字符串片段的数组;
- 其余 n 个参数依次为 Template Literal 中的 n 个 Interpolation;
let a = 6;
let b = 9;
function simpleTag(strings, aValExpression, bValExpression, sumExpression) {
console.log(strings);
console.log(aValExpression);
console.log(bValExpression);
console.log(sumExpression);
return "foobar";
}
let taggedResult = simpleTag`${a} + ${b} = ${a + b}`;
// ["", " + ", " = ", ""]
// 6
// 9
// 15
console.log(taggedResult); // "foobar"
原始字符串
console.log(String.raw`first line\nsecond line`); // "first line\nsecond line"
Symbol 类型
基础
Symbol
- 原子类型;
- Symbol 实例是唯一, 不可变的;
- 避免对象同名字符串;
let sym = Symbol();
// 字符串是对 sym 的描述, 与符号标识无关
let sym = Symbol("test");
符号作为属性
let s1 = Symbol("foo");
// 使用计算属性语法
let o = {
[s1]: "foo val";
};
// 使用 Object.defineProperty()/Object.defineProperties() 方法
Object.defineProperty(o, s1, { value: "foo val" });
Object.defineProperties(o, {
[s1]: { value: "foo val" };
});
方法
- Symbol.for(key): 全局注册 symbol;
- Symbol.keyFor(sym): 查询全局注册表中 symbol 对应的 key 值;
内置符号
Symbol.asyncIterator
- 返回对象的 AsyncIterator, 用于 for-await-of 语句;
class Emitter {
constructor(max) {
this.max = max;
this.asyncIdx = 0;
}
async *[Symbol.asyncIterator]() {
while (this.asyncIdx < this.max) {
yield new Promise((resolve) => resolve(this.asyncIdx++));
}
}
}
Symbol.hasInstance
- 确定一个对象是否使其实例, 用于 instanceof 操作符;
class Bar {}
class Baz extends Bar {
static [Symbol.hasInstance]() {
return false;
}
}
Symbol.isConcatSpreadable
- 表示该对象使用 Array.prototype.concat() 是否打平其数组元素, 默认为 true
let initial = ["foo"];
let array = ["bar"];
console.log(array[Symbol.isConcatSpreadable]); // undefined
console.log(initial.concat(array)); // ['foo', 'bar']
array[Symbol.isConcatSpreadable] = false;
console.log(initial.concat(array)); // ['foo', Array(1)]
Symbol.iterator
- 返回对象的 Iterator, 用于 for-of 语句;
class Emitter {
constructor(max) {
this.max = max;
this.idx = 0;
}
*[Symbol.iterator]() {
while (this.idx < this.max) {
yield this.idx++;
}
}
}
Symbol.match
- 应用于正则表达式匹配, 用于 match/replace/search 等方法, RegExp 默认具有该属性;
class FooMatcher {
static [Symbol.match](target) {
return target.includes("foo");
}
}
Symbol.toPrimitive
- 将对象转换为对应的原始值, 应用与隐式转换;
class Bar {
constructor() {
this[Symbol.toPrimitive] = function (hint) {
switch (hint) {
case "number":
return 3;
case "string":
return "string bar";
case "default":
default:
return "default bar";
}
};
}
}
let bar = new Bar();
console.log(3 + bar); // "3default bar"
console.log(3 - bar); // 0
console.log(String(bar)); // "string bar"
最佳实践
解决浮点数精度问题
Number.EPSILON
- ES6 提供 Number.EPSILON 属性;
- 只要误差小于其, 即可判断相等;
浮点数转换为整数
- 根据实际情况, 将浮点数转换为整数;
数学库
- 使用数学库;
判断数据类型的方法
不同方法优劣
| typeof | instancof | constructor | Object.prototype.toString.call() | |
|---|---|---|---|---|
| 优点 | 简单 | 可判断引用数据类型 | 检查除 null/undefined 的一切类型 | 所有类型 |
| 缺点 | 无法判断引用数据类型和 null | 不能检查基本数据类型, 无法跨 iframe | 不能跨 iframe, constor 可被修改 | 无法在 IE 使用 |
var str = "Hello, World!";
typeof str; // "string"
instanceof str; // false
str.constructor === String; // true
var strType = Object.prototype.toString.call(str); // [object String]
判断空对象
- 使用 Object.keys() 方法;
- 使用 for in;
- 使用 JSON.stringify() 方法;
Object.keys(obj).length === 0;
function isEmpty(obj) {
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
return false;
}
}
return true;
}
JSON.stringify(obj) === "{}";
undefined 和 null 判断
- 使用简单判等;
if (value == null) {
// ...
}
自定义类型判断方法
- typeof 判断基本类型;
- Object.prototype.toString 判断引用类型;
typeof 历史遗留问题
typeof null; // object
typeof NaN; // number
数字千分位分隔
- 数字转换为字符串数组, 三位添加逗号;
- 使用正则表达式;
function numberWithCommas(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
- 使用 Intl.NumberFormat() AP
var number = 1234567.8911111;
var formattedNumber = new Intl.NumberFormat("en-US", {}).format(number);
console.log(formattedNumber); // 1,234,567.891
- 使用 String.toLocaleString() API;
- 实际调用 Intl.NumberFormat() API;
console.log((1234567.8911111).toLocaleString("en-US")); // 1,234,567.891