编程基础-数据类型

数据类型概述

数据类型是编程语言中的基本概念,它定义了数据的性质、存储方式和可以进行的操作。选择正确的数据类型可以提高代码的可读性、性能和可靠性。

为什么需要数据类型

  1. 内存管理:不同类型占用的内存空间不同
  2. 数据验证:防止无效数据的输入和处理
  3. 代码优化:编译器/解释器可以根据类型进行优化
  4. 代码安全:避免类型相关的运行时错误

类型系统分类

静态类型 vs 动态类型

特性 静态类型 动态类型
类型检查时机 编译时 运行时
示例语言 Java, C++, TypeScript JavaScript, Python, Ruby
优点 类型安全,性能好,IDE支持强 开发速度快,灵活性强
缺点 开发较繁琐,学习曲线陡 运行时错误,难以调试

强类型 vs 弱类型

特性 强类型 弱类型
类型转换 严格限制,需要显式转换 允许隐式转换
示例语言 Python, Java JavaScript, PHP
安全性 低,容易产生意外错误

基本数据类型

1. 数值类型

整型 (Integer)

表示整数,如 1, 42, -10

1
2
3
let age = 25;
let count = -5;
let bigNumber = 9007199254740991; // Number.MAX_SAFE_INTEGER

注意事项:

  • 不同语言整型范围不同(8位、16位、32位、64位)
  • 注意整数溢出问题

浮点型 (Float)

表示小数,如 3.14, -0.5

1
2
let pi = 3.14159;
let temperature = -15.5;

浮点数精度问题:

1
2
3
4
5
6
7
8
console.log(0.1 + 0.2); // 0.30000000000000004
console.log(0.1 + 0.2 === 0.3); // false

// 解决方案:使用精度比较或第三方库
function equal(a, b, epsilon = 0.00001) {
return Math.abs(a - b) < epsilon;
}
console.log(equal(0.1 + 0.2, 0.3)); // true

BigInt(大整数)

用于表示超出安全整数范围的大数

1
2
let hugeNumber = 9007199254740992n;
let result = hugeNumber + 1n;

2. 字符串类型 (String)

用于表示文本数据,用引号包裹:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 双引号
let name = "知识库";
let message = "Hello \"World\""; // 转义字符

// 单引号
let greeting = 'Hello World';

// 模板字符串(ES6)
let template = `Hello ${name}, today is ${new Date().toLocaleDateString()}`;
let multiLine = `
第一行
第二行
第三行
`;

常用字符串方法:

1
2
3
4
5
6
7
8
9
let str = "Hello World";

console.log(str.length); // 11
console.log(str.toUpperCase()); // "HELLO WORLD"
console.log(str.toLowerCase()); // "hello world"
console.log(str.substring(0, 5)); // "Hello"
console.log(str.split(" ")); // ["Hello", "World"]
console.log(str.includes("World")); // true
console.log(str.replace("World", "JavaScript")); // "Hello JavaScript"

3. 布尔类型 (Boolean)

只有两个值:truefalse

1
2
3
4
5
6
7
8
9
10
11
12
let isTrue = true;
let isFalse = false;

// 布尔转换规则
console.log(Boolean(0)); // false
console.log(Boolean(1)); // true
console.log(Boolean("")); // false
console.log(Boolean("text")); // true
console.log(Boolean(null)); // false
console.log(Boolean(undefined)); // false
console.log(Boolean({})); // true
console.log(Boolean([])); // true

4. 空值类型

null

表示”无值”或”空值”,是显式设置的空值

1
2
3
4
let empty = null;
console.log(typeof null); // "object" (历史遗留问题)
console.log(null === undefined); // false
console.log(null == undefined); // true

undefined

表示变量已声明但未赋值

1
2
3
4
5
6
let notAssigned;
console.log(notAssigned); // undefined
console.log(typeof undefined); // "undefined"

function noReturn() {}
console.log(noReturn()); // undefined

null vs undefined 的区别:

1
2
3
4
5
6
// null:主动设置为空
let data = null;

// undefined:未初始化或未找到
let undefinedVar;
console.log(Object.nonExistent); // undefined

5. Symbol(符号,ES6新增)

表示唯一的、不可变的数据类型,常用于对象属性键

1
2
3
4
5
6
7
8
9
const id1 = Symbol("id");
const id2 = Symbol("id");
console.log(id1 === id2); // false

// 用作对象属性键
const obj = {
[id1]: "value1",
[id2]: "value2"
};

复合数据类型

1. 数组 (Array)

有序的数据集合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 创建数组
let fruits = ["apple", "banana", "orange"];
let numbers = [1, 2, 3, 4, 5];
let mixed = [1, "text", true, null];

// 常用方法
console.log(fruits.length); // 3
console.log(fruits[0]); // "apple"
console.log(fruits.push("grape")); // 添加元素
console.log(fruits.pop()); // 删除最后一个元素
console.log(fruits.shift()); // 删除第一个元素
console.log(fruits.unshift("pear")); // 在开头添加元素

// 数组遍历
fruits.forEach((fruit, index) => {
console.log(`${index}: ${fruit}`);
});

// 数组方法
console.log(fruits.map(f => f.toUpperCase())); // 转大写
console.log(fruits.filter(f => f.length > 5)); // 过滤
console.log(fruits.reduce((acc, curr) => acc + curr, "")); // 累加

2. 对象 (Object)

键值对的无序集合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 创建对象
let person = {
name: "张三",
age: 25,
city: "北京",
greet: function() {
console.log(`你好,我是${this.name}`);
}
};

// 访问属性
console.log(person.name); // "张三"
console.log(person["age"]); // 25

// 动态属性
let key = "city";
console.log(person[key]); // "北京"

// 添加/修改属性
person.email = "zhangsan@example.com";
person.age = 26;

// 删除属性
delete person.email;

// 对象方法
console.log(Object.keys(person)); // ["name", "age", "city", "greet"]
console.log(Object.values(person)); // ["张三", 25, "北京", function]
console.log(Object.entries(person)); // [["name", "张三"], ...]

// 对象解构
const { name, age } = person;

3. 函数 (Function)

函数也是一种数据类型,可以赋值给变量、作为参数传递

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 函数声明
function add(a, b) {
return a + b;
}

// 函数表达式
const multiply = function(a, b) {
return a * b;
};

// 箭头函数
const divide = (a, b) => a / b;

// 函数作为参数
function operate(a, b, operation) {
return operation(a, b);
}

console.log(operate(5, 3, add)); // 8
console.log(operate(6, 2, multiply)); // 12

数据类型转换

隐式转换

JavaScript在某些操作中会自动进行类型转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 数字运算中的字符串
let num = "5";
let result = num * 2; // 10(字符串转数字)
let addResult = num + 2; // "52"(数字转字符串)

// 布尔转换
console.log(1 == true); // true
console.log(0 == false); // true
console.log("" == false); // true
console.log("5" == 5); // true(类型不同,值相同)

// 比较运算
console.log("10" > 5); // true
console.log("abc" > 5); // false

隐式转换的危险:

1
2
3
4
5
6
7
8
9
// 不推荐的隐式转换
let value = "0";
if (value) { // true(非空字符串为真)
console.log("值为真");
}

if (value == false) { // true(隐式转换)
console.log("等于false");
}

显式转换

使用专门的函数进行类型转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 转为字符串
let num = 123;
let str1 = String(num); // "123"
let str2 = num.toString(); // "123"
let str3 = "" + num; // "123"(隐式)
let str4 = `${num}`; // "123"(模板字符串)

// 转为数字
let text = "456";
let num1 = Number(text); // 456
let num2 = parseInt(text); // 456(整数)
let num3 = parseFloat("3.14"); // 3.14(浮点数)
let num4 = +text; // 456(一元运算符)

// 转为布尔值
let bool1 = Boolean(1); // true
let bool2 = !!1; // true(双重否定)

转换失败处理:

1
2
3
4
5
6
7
8
9
console.log(Number("hello"));      // NaN
console.log(parseInt("hello")); // NaN
console.log(Number("")); // 0
console.log(Number(null)); // 0
console.log(Number(undefined)); // NaN

// 检查NaN
console.log(isNaN(NaN)); // true
console.log(Number.isNaN(NaN)); // true(更推荐)

类型检查

typeof 运算符

1
2
3
4
5
6
7
8
9
console.log(typeof 42);            // "number"
console.log(typeof "hello"); // "string"
console.log(typeof true); // "boolean"
console.log(typeof undefined); // "undefined"
console.log(typeof null); // "object"(注意!)
console.log(typeof {}); // "object"
console.log(typeof []); // "object"
console.log(typeof function(){}); // "function"
console.log(typeof Symbol()); // "symbol"

instanceof 运算符

检查对象是否是某个类的实例

1
2
3
4
5
6
7
let arr = [1, 2, 3];
let obj = { name: "test" };

console.log(arr instanceof Array); // true
console.log(arr instanceof Object); // true
console.log(obj instanceof Object); // true
console.log(obj instanceof Array); // false

准确的类型检查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 检查数组
const isArray = Array.isArray([1, 2, 3]); // true

// 准确检查null
const isNull = value => value === null;

// 检查 undefined
const isUndefined = value => value === undefined;

// 完整的类型检查函数
function getType(value) {
if (value === null) return 'null';
if (Array.isArray(value)) return 'array';
return typeof value;
}

console.log(getType(null)); // "null"
console.log(getType([])); // "array"
console.log(getType({})); // "object"
console.log(getType(42)); // "number"

常见错误和解决方案

1. 类型混淆错误

问题:

1
let result = "10" + 5;  // "105" 而不是 15

解决方案:

1
2
3
let result = Number("10") + 5;  // 15
// 或者
let result = parseInt("10") + 5; // 15

2. NaN 的处理

问题:

1
2
let result = "hello" * 5;  // NaN
console.log(result === NaN); // false!

解决方案:

1
2
3
4
5
6
7
8
9
let result = "hello" * 5;
if (isNaN(result)) {
console.log("计算结果无效");
}

// 更安全的检查
if (Number.isNaN(result)) {
console.log("结果确实是 NaN");
}

3. 空值检查

问题:

1
2
let data;
console.log(data.length); // TypeError

解决方案:

1
2
3
4
5
6
7
8
// 可选链操作符(ES2020)
console.log(data?.length); // undefined

// 空值合并操作符(ES2020)
let length = data?.length ?? 0;

// 传统方法
let length = data ? data.length : 0;

4. 数组 vs 对象混淆

问题:

1
2
let list = {};
list.push("item"); // TypeError: list.push is not a function

解决方案:

1
2
3
4
5
6
7
let list = [];
list.push("item"); // 正确

// 使用前检查类型
if (Array.isArray(list)) {
list.push("item");
}

性能优化建议

1. 避免频繁的类型转换

1
2
3
4
5
6
7
8
9
10
// 不推荐
for (let i = 0; i < 1000000; i++) {
result = Number("123") * i;
}

// 推荐
const num = Number("123");
for (let i = 0; i < 1000000; i++) {
result = num * i;
}

2. 使用适当的数据类型

1
2
3
4
5
6
7
8
9
// 字符串连接:数组join比+更快
let parts = ["Hello", "World", "from", "JavaScript"];
let result = parts.join(" ");

// 数字运算:避免不必要的字符串转换
let sum = 0;
for (let num of numbers) {
sum += num; // 直接数字相加
}

3. 类型缓存

1
2
3
4
5
6
7
8
9
10
11
// 缓存类型检查结果
function processValue(value) {
const isString = typeof value === 'string';
const isNumber = typeof value === 'number';

if (isString) {
// 字符串处理
} else if (isNumber) {
// 数字处理
}
}

跨语言数据类型对比

数据类型 JavaScript Python Java C++
整型 Number int int, long, short int, long, long long
浮点型 Number float float, double float, double
字符串 String str String std::string
布尔型 Boolean bool boolean bool
数组 Array list, tuple Array std::vector, std::array
对象/字典 Object dict HashMap std::map
空值 null, undefined None null nullptr
无类型 - Any Object void*

最佳实践

1. 类型一致性

1
2
3
4
5
6
7
8
9
10
// 推荐:保持类型一致
function calculateSum(numbers) {
let sum = 0; // 明确初始化为数字
for (let num of numbers) {
if (typeof num === 'number') {
sum += num;
}
}
return sum;
}

2. 类型检查和验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 函数参数验证
function processUserData(data) {
if (typeof data !== 'object' || data === null) {
throw new Error('数据必须是对象');
}

if (!data.name || typeof data.name !== 'string') {
throw new Error('name 必须是非空字符串');
}

if (typeof data.age !== 'number' || data.age < 0) {
throw new Error('age 必须是非负数');
}

// 处理数据
}

3. 使用 TypeScript 提供类型安全

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
interface User {
name: string;
age: number;
email?: string; // 可选属性
}

function greetUser(user: User): string {
return `你好,${user.name},今年${user.age}岁`;
}

// 编译时类型检查
const user: User = {
name: "张三",
age: 25
};

4. 防御性编程

1
2
3
4
5
6
7
8
9
// 使用可选链和空值合并
function getUserName(user) {
return user?.profile?.name ?? "匿名用户";
}

// 使用默认值
function processList(list = []) {
return list.filter(item => item.isValid);
}

5. 代码文档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 计算数组中所有数字的平均值
* @param {number[]} numbers - 数字数组
* @returns {number} 平均值
* @throws {Error} 如果输入不是数组或包含非数字元素
*/
function calculateAverage(numbers) {
if (!Array.isArray(numbers)) {
throw new Error('输入必须是数组');
}

if (numbers.length === 0) {
return 0;
}

const sum = numbers.reduce((acc, num) => {
if (typeof num !== 'number' || isNaN(num)) {
throw new Error('数组必须只包含有效数字');
}
return acc + num;
}, 0);

return sum / numbers.length;
}

进阶话题

1. 类型推断

现代编程语言可以自动推断变量类型

1
2
3
4
5
6
7
// JavaScript: 动态类型,无需声明
let x = 10; // 推断为 number
let y = "hello"; // 推断为 string

// TypeScript: 类型推断
let inferred = 42; // 推断为 number 类型
const constant = "text"; // 推断为 "text" 字面量类型

2. 泛型(TypeScript)

1
2
3
4
5
6
7
8
9
function identity<T>(arg: T): T {
return arg;
}

const num = identity<number>(42); // 返回 number
const str = identity<string>("hello"); // 返回 string

// 类型推断
const bool = identity(true); // 返回 boolean

3. 联合类型

1
2
3
4
5
6
7
8
9
// TypeScript 联合类型
type ID = string | number;

function printID(id: ID) {
console.log(`ID: ${id}`);
}

printID(123); // 有效
printID("abc-123"); // 有效

4. 类型守卫

1
2
3
4
5
6
7
8
9
function processValue(value: string | number) {
if (typeof value === 'string') {
// 这里 value 被推断为 string
console.log(value.toUpperCase());
} else {
// 这里 value 被推断为 number
console.log(value.toFixed(2));
}
}