1.1 基本数据类型(Primitive Types)

3 阅读11分钟

1.1.1 Number类型

概念解释

  • 定义:表示整数和浮点数的数字类型
  • 特点:采用IEEE 754标准的64位双精度浮点数
  • 范围:-(2^53 - 1) 到 (2^53 - 1)

详细说明

// 1. 数字字面量
const integer = 42;           // 整数
const float = 42.42;         // 浮点数
const scientific = 3e8;      // 科学计数法
const binary = 0b1010;      // 二进制
const octal = 0o744;        // 八进制
const hex = 0xFF;           // 十六进制

// 2. 特殊值
const infinity = Infinity;   // 无穷大
const negInfinity = -Infinity; // 负无穷大
const notANumber = NaN;     // 非数字

// 3. 数字操作
const sum = 0.1 + 0.2;      // 注意:浮点数精度问题
console.log(sum);           // 0.30000000000000004

// 4. 数字方法
const num = 42.42;
console.log(num.toFixed(1));        // "42.4"
console.log(num.toPrecision(3));    // "42.4"
console.log(Number.isInteger(num)); // false

常见错误及最佳实践

// 错误示例1:浮点数比较
if (0.1 + 0.2 === 0.3) {  // 永远不会为true
    console.log("相等");
}

// 正确做法:使用极小值比较
const epsilon = 0.00001;
if (Math.abs((0.1 + 0.2) - 0.3) < epsilon) {
    console.log("近似相等");
}

// 错误示例2:检查NaN
if (someValue === NaN) {  // 永远不会为true
    console.log("是NaN");
}

// 正确做法:使用Number.isNaN()
if (Number.isNaN(someValue)) {
    console.log("是NaN");
}

// 错误示例3:数字运算溢出
const bigNumber = 9999999999999999; // 精度丢失

// 正确做法:使用BigInt
const safeBigNumber = 9999999999999999n;

注意事项

  1. 精度限制

    • 整数精确表示范围:±2^53
    • 浮点数计算可能存在精度误差
    • 需要高精度计算时考虑使用专门的库(如decimal.js)
  2. 性能考虑

    • 整数运算比浮点数运算快
    • 位运算仅适用于32位整数
    • 大数运算考虑使用BigInt
  3. 类型转换

// 字符串转数字
const num1 = Number("42");     // 推荐
const num2 = +"42";           // 简写方式
const num3 = parseInt("42");   // 解析整数
const num4 = parseFloat("42.42"); // 解析浮点数

// 数字转字符串
const str1 = String(42);      // 推荐
const str2 = 42 + "";         // 不推荐:隐式转换
const str3 = (42).toString(); // 对象方法

扩展知识

  1. Number对象的静态属性
Number.MAX_VALUE          // 最大数值
Number.MIN_VALUE          // 最小数值
Number.MAX_SAFE_INTEGER   // 最大安全整数
Number.MIN_SAFE_INTEGER   // 最小安全整数
Number.EPSILON            // 最小精度
  1. 数字格式化
// 使用Intl.NumberFormat进行本地化格式化
const formatter = new Intl.NumberFormat('zh-CN', {
    style: 'currency',
    currency: 'CNY'
});

console.log(formatter.format(42.42)); // ¥42.42

// 科学计数法
const bigNum = 1e6;
console.log(bigNum.toExponential()); // "1e+6"
  1. 现代JavaScript特性
// 数字分隔符(ES2021)
const billion = 1_000_000_000;  // 更易读的数字表示

// BigInt用于大数运算
const bigInt = 9007199254740991n;
const result = bigInt + 1n;

实际应用场景

  1. 金融计算
// 不推荐
const price = 0.1 + 0.2;

// 推荐:使用整数计算后再转换
function calculatePrice(price1, price2) {
    // 转换为分进行计算
    const cents1 = Math.round(price1 * 100);
    const cents2 = Math.round(price2 * 100);
    const totalCents = cents1 + cents2;
    return totalCents / 100;
}

console.log(calculatePrice(0.1, 0.2)); // 0.3
  1. 科学计算
// 计算圆的面积
function calculateCircleArea(radius) {
    if (typeof radius !== 'number' || radius < 0) {
        throw new Error('半径必须是正数');
    }
    return Math.PI * Math.pow(radius, 2);
}

// 带精度控制的计算
function calculateWithPrecision(value, precision = 2) {
    return Number(Math.round(value + 'e' + precision) + 'e-' + precision);
}

1.1.2 String类型

概念解释

  • 定义:表示文本数据的字符序列
  • 特点:不可变(immutable)、使用UTF-16编码
  • 使用场景:文本处理、数据展示、模板生成

详细说明

1. 字符串创建
// 1. 字符串字面量
const single = 'single quotes';
const double = "double quotes";
const backticks = `template literal`;

// 2. String构造函数
const strObject = new String("string"); // 不推荐:会创建String对象
const strPrimitive = String("string");  // 推荐:显式转换为字符串

// 3. 模板字符串
const name = "张三";
const age = 25;
const greeting = `你好,我是${name},今年${age}岁`; // 支持插值表达式

// 4. 多行字符串
const multiLine = `
    这是第一行
    这是第二行
    这是第三行
`; // 保留换行和缩进

// 5. 转义字符
const escaped = 'I\'m a string'; // 使用反斜杠转义
const newLine = 'First line\nSecond line'; // 特殊字符
2. 字符串操作方法
const str = "Hello, World!";

// 1. 基本属性和访问
console.log(str.length);     // 13
console.log(str[0]);         // "H"
console.log(str.charAt(0));  // "H"(推荐)

// 2. 查找和位置
console.log(str.indexOf("o"));      // 4
console.log(str.lastIndexOf("o"));  // 7
console.log(str.includes("World")); // true
console.log(str.startsWith("Hello")); // true
console.log(str.endsWith("!")); // true

// 3. 提取子串
console.log(str.substring(0, 5));  // "Hello"
console.log(str.slice(0, 5));      // "Hello"
console.log(str.slice(-6));        // "World!"

// 4. 转换和替换
console.log(str.toUpperCase());    // "HELLO, WORLD!"
console.log(str.toLowerCase());    // "hello, world!"
console.log(str.replace("Hello", "Hi")); // "Hi, World!"
console.log(str.replaceAll("l", "L")); // "HeLLo, WorLd!"

// 5. 分割和合并
console.log(str.split(", "));      // ["Hello", "World!"]
console.log(["Hello", "World"].join(" ")); // "Hello World"

// 6. 去除空白
const spacedStr = "  trim me  ";
console.log(spacedStr.trim());     // "trim me"
console.log(spacedStr.trimStart()); // "trim me  "
console.log(spacedStr.trimEnd());   // "  trim me"
3. 高级字符串操作
// 1. 正则表达式匹配
const text = "Hello 123 World 456";
console.log(text.match(/\d+/g));   // ["123", "456"]
console.log(text.matchAll(/\d+/g)); // 返回迭代器

// 2. Unicode支持
const emoji = "😀";
console.log(emoji.length);         // 2(因为使用UTF-16编码)
console.log([...emoji].length);    // 1(正确的字符数)

// 3. 字符串填充
const num = "42";
console.log(num.padStart(5, "0")); // "00042"
console.log(num.padEnd(5, "0"));   // "42000"

// 4. 重复字符串
console.log("abc".repeat(3));      // "abcabcabc"

常见错误及最佳实践:

// 错误示例1:字符串连接
let result = "";
for (let i = 0; i < 1000; i++) {
    result += i; // 性能差:每次连接都创建新字符串
}

// 正确做法:使用数组join或模板字符串
const numbers = Array.from({length: 1000}, (_, i) => i);
result = numbers.join("");
// 或使用数组的reduce
result = numbers.reduce((acc, curr) => `${acc}${curr}`, "");

// 错误示例2:不当的字符串比较
if (userInput.toLowerCase() == "yes") { // 不推荐
    // ...
}

// 正确做法:规范化后使用严格相等
const normalizedInput = userInput.trim().toLowerCase();
if (normalizedInput === "yes") {
    // ...
}

// 错误示例3:使用eval解析字符串
const jsonStr = '{"name": "张三"}';
const obj = eval(jsonStr); // 危险:可能执行恶意代码

// 正确做法:使用JSON.parse
const safeObj = JSON.parse(jsonStr);

性能优化

// 1. 大量字符串拼接
const fragments = [];
for (let i = 0; i < 1000; i++) {
    fragments.push(`Item ${i}`);
}
const result = fragments.join("");

// 2. 字符串搜索优化
const longText = "很长的文本...";
// 创建正则表达式对象一次
const regex = /pattern/g;
while (regex.exec(longText) !== null) {
    // 处理匹配
}

// 3. 字符串缓存
const stringCache = new Map();
function getProcessedString(str) {
    if (!stringCache.has(str)) {
        stringCache.set(str, expensiveStringOperation(str));
    }
    return stringCache.get(str);
}

实际应用场景

// 1. URL处理
function parseQueryString(url) {
    const queryString = url.split('?')[1];
    if (!queryString) return {};
    
    return queryString
        .split('&')
        .reduce((params, param) => {
            const [key, value] = param.split('=');
            params[decodeURIComponent(key)] = decodeURIComponent(value);
            return params;
        }, {});
}

// 2. 模板引擎简单实现
function templateFn(str, data) {
    return str.replace(/\${(\w+)}/g, (match, key) => {
        return data[key] || '';
    });
}

const template = "Hello, ${name}! You are ${age} years old.";
const data = { name: "张三", age: 25 };
console.log(templateFn(template, data)); // Hello, 张三! You are 25 years old.

// 3. 文本格式化
function formatNumber(num) {
    return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}

console.log(formatNumber(1234567)); // "1,234,567"

注意事项

  1. 字符串不可变性

    • 所有字符串操作都会返回新字符串
    • 原字符串永远不会被修改
    • 频繁的字符串操作要注意性能
  2. 编码问题

    • 使用UTF-16编码
    • 某些字符(如emoji)占用两个代码单元
    • 使用专门的库处理复杂的Unicode操作
  3. 安全考虑

    • 避免使用eval()处理字符串
    • 注意XSS攻击风险
    • 使用适当的转义处理用户输入

1.1.3 Boolean类型

概念解释:

  • 定义:表示逻辑值,只有true和false两个值
  • 特点:用于条件判断和逻辑运算
  • 使用场景:条件控制、标志位、状态判断

详细说明

1. Boolean值的创建和转换
// 1. 直接声明
const isActive = true;
const isLoading = false;

// 2. Boolean构造函数
const boolObject = new Boolean(true); // 不推荐:创建Boolean对象
const boolPrimitive = Boolean(1);     // 推荐:显式转换

// 3. 双重否定转换
const isTrue = !!"非空字符串";    // true
const isFalse = !!"";           // false

// 4. 各种类型转换为布尔值的规则
console.log(Boolean(""));        // false
console.log(Boolean("0"));       // true
console.log(Boolean(0));         // false
console.log(Boolean(NaN));       // false
console.log(Boolean(null));      // false
console.log(Boolean(undefined)); // false
console.log(Boolean({}));        // true
console.log(Boolean([]));        // true
2. 逻辑运算
// 1. 基本逻辑运算
const a = true;
const b = false;

console.log(a && b);  // false
console.log(a || b);  // true
console.log(!a);      // false

// 2. 短路运算
const obj = null;
const name = obj && obj.name;  // null(安全访问)
const defaultName = name || "默认名称";  // "默认名称"

// 3. 空值合并运算符
const value = null;
const result = value ?? "默认值";  // "默认值"
3. 条件判断最佳实践
// 1. 显式布尔检查
function isValidUser(user) {
    // 明确的布尔返回值
    return Boolean(
        user &&
        user.id &&
        user.name &&
        user.email
    );
}

// 2. 使用Array.some()和Array.every()
const numbers = [1, 2, 3, 4, 5];
const hasEven = numbers.some(num => num % 2 === 0);  // true
const allPositive = numbers.every(num => num > 0);   // true

// 3. 状态标志管理
class TaskManager {
    #isRunning = false;
    
    start() {
        if (this.#isRunning) {
            throw new Error('Task is already running');
        }
        this.#isRunning = true;
        // 执行任务...
    }
    
    stop() {
        this.#isRunning = false;
    }
}

常见错误及最佳实践:

// 错误示例1:隐式类型转换
if ("0") {
    console.log("这会执行,因为非空字符串转换为true");
}

// 正确做法:显式转换或比较
if (value === "0") {
    console.log("精确的字符串比较");
}

// 错误示例2:误用逻辑运算符
const value = 0;
const result = value || 42;  // 会返回42,可能不是想要的结果

// 正确做法:使用空值合并运算符
const safeResult = value ?? 42;  // 会返回0

// 错误示例3:复杂条件判断
if (value && value.length && value.length > 0 && value[0]) {
    // 难以维护的条件判断
}

// 正确做法:封装判断逻辑
function isValidValue(value) {
    return Array.isArray(value) && value.length > 0 && Boolean(value[0]);
}

if (isValidValue(value)) {
    // 更清晰的代码
}

1.1.4 Null类型

概念解释

  • 定义:表示"空"或"不存在"的特殊值
  • 特点:只有一个值null
  • 使用场景:表示对象的空值、初始化变量、函数返回值

详细说明

// 1. null的基本使用
let user = null;  // 明确表示对象不存在

// 2. null的类型检查
console.log(typeof null);  // "object"(JavaScript的一个历史遗留bug)
console.log(Object.prototype.toString.call(null));  // "[object Null]"

// 3. null的相等性
console.log(null == undefined);   // true(宽松相等)
console.log(null === undefined);  // false(严格相等)

// 4. 空值合并运算符与null
const value = null;
const result = value ?? "default";  // "default"

最佳实践

// 1. 初始化对象引用
class UserManager {
    #currentUser = null;  // 明确表示没有当前用户
    
    login(user) {
        this.#currentUser = user;
    }
    
    logout() {
        this.#currentUser = null;  // 显式清除引用
    }
}

// 2. 函数返回值
function findUser(id) {
    const user = database.find(id);
    return user || null;  // 明确返回null表示未找到
}

// 3. 参数验证
function processUser(user) {
    if (user === null) {
        throw new Error('User cannot be null');
    }
    // 处理用户...
}

1.1.5 Undefined类型

概念解释

  • 定义:表示未定义或不存在的值
  • 特点:全局只有一个undefined值
  • 使用场景:变量未赋值、函数无返回值、对象不存在的属性

详细说明

// 1. undefined的产生情况
let variable;  // 自动获得undefined值
function noReturn() {}  // 函数隐式返回undefined
const obj = {};
console.log(obj.nonexistent);  // undefined

// 2. undefined的检查
console.log(typeof undefined);  // "undefined"
console.log(void 0);           // undefined(获取undefined的安全方式)

// 3. 可选链操作符
const user = undefined;
console.log(user?.name);  // undefined(安全访问)

最佳实践和注意事项

// 1. 参数默认值
function greet(name = "Guest") {
    console.log(`Hello, ${name}!`);
}

// 2. 属性检查
const obj = { a: 1 };

// 不推荐
if (obj.b !== undefined) {
    // 处理...
}

// 推荐
if ('b' in obj) {
    // 处理...
}

// 3. undefined vs null的使用
// undefined:表示系统级别的、出乎意料的或类型错误的值
// null:表示程序级别的、意料之中的或正常情况的空值

// 示例
class Cache {
    get(key) {
        const value = this.storage.get(key);
        return value === undefined ? null : value;
    }
    
    set(key, value) {
        if (value === null) {
            throw new Error('Value cannot be null');
        }
        this.storage.set(key, value);
    }
}

安全考虑和防御性编程

// 1. 安全的undefined检查
function isUndefined(value) {
    return value === void 0;
}

// 2. 防御性编程示例
function processData(data) {
    // 参数验证
    if (data === undefined) {
        throw new TypeError('Data is required');
    }
    
    // 安全的属性访问
    const name = data?.name ?? 'Unknown';
    const age = data?.age ?? 0;
    
    // 类型检查
    if (typeof name !== 'string') {
        throw new TypeError('Name must be a string');
    }
    
    if (typeof age !== 'number') {
        throw new TypeError('Age must be a number');
    }
    
    return {
        name,
        age
    };
}

让我们继续讲解Symbol和BigInt这两种现代JavaScript的基本数据类型。

1.1.6 Symbol类型

概念解释

  • 定义:表示唯一的标识符
  • 特点:每个Symbol值都是独一无二的
  • 使用场景:唯一属性键、常量值、防止属性名冲突

详细说明

1. Symbol的创建和使用
// 1. 基本创建
const sym1 = Symbol();
const sym2 = Symbol('description');  // 带描述的Symbol
const sym3 = Symbol('description');  // 注意:sym2 !== sym3

// 2. Symbol.for - 全局Symbol注册
const globalSym1 = Symbol.for('globalKey');
const globalSym2 = Symbol.for('globalKey');
console.log(globalSym1 === globalSym2);  // true

// 3. 获取Symbol描述
console.log(sym2.description);  // "description"
console.log(Symbol.keyFor(globalSym1));  // "globalKey"

// 4. Symbol作为对象属性
const MY_KEY = Symbol('my_key');
const obj = {
    [MY_KEY]: 'value',
    regularKey: 'regular value'
};

// 访问Symbol属性
console.log(obj[MY_KEY]);  // "value"
2. Symbol的高级用法
// 1. 内置Symbol值
class CustomIterator {
    // 自定义迭代器
    [Symbol.iterator]() {
        let count = 0;
        return {
            next() {
                return count < 3 
                    ? { value: count++, done: false }
                    : { done: true };
            }
        };
    }
    
    // 自定义类型转换
    [Symbol.toPrimitive](hint) {
        switch (hint) {
            case 'number':
                return 123;
            case 'string':
                return 'CustomIterator';
            default:
                return true;
        }
    }
}

// 2. Symbol用于私有属性(ES2019之前的方案)
const _private = Symbol('private');
class MyClass {
    constructor() {
        this[_private] = 'private value';
    }
    
    getPrivateValue() {
        return this[_private];
    }
}

// 3. Symbol.species
class SpecialArray extends Array {
    static get [Symbol.species]() {
        return Array; // 确保map等操作返回普通数组
    }
}
3. 实际应用场景
// 1. 状态管理中的action类型
const ActionTypes = {
    INCREMENT: Symbol('INCREMENT'),
    DECREMENT: Symbol('DECREMENT'),
    RESET: Symbol('RESET')
};

function reducer(state, action) {
    switch (action.type) {
        case ActionTypes.INCREMENT:
            return state + 1;
        case ActionTypes.DECREMENT:
            return state - 1;
        case ActionTypes.RESET:
            return 0;
        default:
            return state;
    }
}

// 2. 插件系统中的钩子
class Plugin {
    static hooks = {
        BEFORE_INIT: Symbol('BEFORE_INIT'),
        AFTER_INIT: Symbol('AFTER_INIT'),
        BEFORE_DESTROY: Symbol('BEFORE_DESTROY')
    };
    
    constructor() {
        this.listeners = new Map();
    }
    
    addHook(hook, callback) {
        if (!this.listeners.has(hook)) {
            this.listeners.set(hook, new Set());
        }
        this.listeners.get(hook).add(callback);
    }
}

// 3. 元数据存储
const metadata = Symbol('metadata');
class User {
    constructor(name) {
        this[metadata] = {
            created: Date.now(),
            id: Math.random().toString(36).substr(2, 9)
        };
        this.name = name;
    }
    
    getMetadata() {
        return this[metadata];
    }
}

注意事项和最佳实践:

// 1. Symbol属性的遍历
const obj = {
    [Symbol('a')]: 'a',
    [Symbol('b')]: 'b',
    c: 'c'
};

// Symbols不会出现在普通遍历中
console.log(Object.keys(obj));  // ['c']
console.log(Object.getOwnPropertyNames(obj));  // ['c']

// 获取Symbol属性
console.log(Object.getOwnPropertySymbols(obj));  // [Symbol(a), Symbol(b)]
console.log(Reflect.ownKeys(obj));  // [Symbol(a), Symbol(b), 'c']

// 2. Symbol与JSON
const objWithSymbol = {
    [Symbol('key')]: 'value',
    regular: 'normal'
};

// Symbol属性会被忽略
console.log(JSON.stringify(objWithSymbol));  // {"regular":"normal"}

// 3. 内存管理
// 不使用Symbol.for时,Symbol不会被垃圾回收
const potentialLeak = [];
function createSymbol() {
    const sym = Symbol('temp');
    potentialLeak.push(sym);
}

// 使用WeakMap存储Symbol相关数据
const symbolData = new WeakMap();
function safeSymbolData(sym, data) {
    symbolData.set(sym, data);
}

1.1.7 BigInt类型

概念解释

  • 定义:表示任意精度的整数
  • 特点:可以安全地存储和操作大整数
  • 使用场景:大数计算、精确时间戳、加密计算

详细说明

1. BigInt的创建和基本操作
// 1. 创建BigInt
const bigInt1 = 9007199254740991n;  // 使用n后缀
const bigInt2 = BigInt("9007199254740991");  // 使用构造函数
const bigIntFromNumber = BigInt(Number.MAX_SAFE_INTEGER);

// 2. 基本运算
const sum = bigInt1 + bigInt2;
const product = bigInt1 * 2n;
const power = bigInt1 ** 2n;

// 3. 比较操作
console.log(1n < 2n);  // true
console.log(2n > 1);   // true
console.log(2n == 2);  // true
console.log(2n === 2); // false(严格相等会检查类型)
2. 实际应用场景
// 1. 精确时间计算
class PreciseTimer {
    static now() {
        // 获取纳秒级时间戳
        return BigInt(process.hrtime.bigint());
    }
    
    static calculateDuration(start, end) {
        return (end - start) / BigInt(1000000); // 转换为毫秒
    }
}

// 2. 大数运算
function factorial(n) {
    if (n === 0n) return 1n;
    return n * factorial(n - 1n);
}

console.log(factorial(50n));

// 3. 金融计算(处理大额数字)
class FinancialCalculator {
    static toCents(dollars) {
        return BigInt(Math.round(dollars * 100));
    }
    
    static fromCents(cents) {
        return Number(cents) / 100;
    }
    
    static addMoney(amount1, amount2) {
        const cents1 = this.toCents(amount1);
        const cents2 = this.toCents(amount2);
        return this.fromCents(cents1 + cents2);
    }
}