JavaScript学习教程,从入门到精通,JavaScript 数据类型详解(7)
JavaScript 数据类型详解
JavaScript 是一种动态类型语言,这意味着变量可以持有任何类型的数据,并且可以在程序执行过程中改变其数据类型。JavaScript 有以下基本数据类型:
1. Undefined 类型
Undefined 类型只有一个值,即 undefined
。当变量被声明但未被赋值时,它的值就是 undefined
。
// Undefined 类型示例
let undeclaredVar; // 声明但未赋值的变量
let undefinedVar = undefined; // 显式赋值为undefined
console.log(undeclaredVar); // 输出: undefined
console.log(undefinedVar); // 输出: undefined
console.log(typeof undeclaredVar); // 输出: "undefined"
// 函数没有返回值时,默认返回undefined
function noReturn() {
// 没有return语句
}
console.log(noReturn()); // 输出: undefined
// 访问对象不存在的属性
const obj = { name: "Alice" };
console.log(obj.age); // 输出: undefined
2. Null 类型
Null 类型也只有一个值,即 null
。它表示一个空对象指针,通常用于表示有意地缺少任何对象值。
// Null 类型示例
let nullVar = null; // 显式赋值为null
console.log(nullVar); // 输出: null
console.log(typeof nullVar); // 输出: "object" (这是JavaScript的一个历史遗留问题)
// 使用场景:初始化一个变量,稍后将赋值为对象
let futureObject = null;
futureObject = { name: "Bob" };
// 清空对象引用
let person = { name: "Charlie" };
person = null; // 断开引用,原对象会被垃圾回收
// 与undefined的区别
console.log(null == undefined); // 输出: true (宽松相等)
console.log(null === undefined); // 输出: false (严格相等)
3. Boolean 类型
Boolean 类型有两个值:true
和 false
,用于表示逻辑实体。
// Boolean 类型示例
let isTrue = true;
let isFalse = false;
console.log(isTrue); // 输出: true
console.log(isFalse); // 输出: false
console.log(typeof isTrue); // 输出: "boolean"
// 布尔转换
console.log(Boolean(1)); // 输出: true (非零数字为true)
console.log(Boolean(0)); // 输出: false (零为false)
console.log(Boolean("")); // 输出: false (空字符串为false)
console.log(Boolean("hello")); // 输出: true (非空字符串为true)
console.log(Boolean(null)); // 输出: false
console.log(Boolean(undefined)); // 输出: false
console.log(Boolean({})); // 输出: true (对象总是true)
// 逻辑运算符
console.log(true && false); // 输出: false (逻辑与)
console.log(true || false); // 输出: true (逻辑或)
console.log(!true); // 输出: false (逻辑非)
// 条件语句中的使用
if (isTrue) {
console.log("This will be executed");
} else {
console.log("This won't be executed");
}
4. Number 类型
Number 类型用于表示整数和浮点数。JavaScript 使用 IEEE 754 标准来表示数字。
// Number 类型示例
let integer = 42; // 整数
let float = 3.14159; // 浮点数
let scientific = 1.23e5; // 科学计数法 (1.23 × 10^5 = 123000)
let hex = 0xff; // 十六进制 (255)
let octal = 0o10; // 八进制 (8)
let binary = 0b1010; // 二进制 (10)
console.log(integer, float, scientific, hex, octal, binary);
// 特殊数值
let infinity = Infinity; // 无穷大
let negativeInfinity = -Infinity; // 负无穷大
let notANumber = NaN; // 非数字 (Not a Number)
console.log(infinity, negativeInfinity, notANumber);
console.log(typeof infinity); // 输出: "number"
// NaN 的特性
console.log(NaN === NaN); // 输出: false (NaN不等于任何值,包括它自己)
console.log(isNaN(NaN)); // 输出: true (使用isNaN函数检测)
console.log(isNaN("hello")); // 输出: true (字符串"hello"转换为数字是NaN)
console.log(Number.isNaN(NaN)); // 输出: true (ES6更安全的检测方法)
console.log(Number.isNaN("hello")); // 输出: false (不会强制转换)
// 数值范围
console.log(Number.MAX_VALUE); // 最大正数 (~1.79e+308)
console.log(Number.MIN_VALUE); // 最小正数 (~5e-324)
console.log(Number.MAX_SAFE_INTEGER); // 最大安全整数 (2^53 - 1)
console.log(Number.MIN_SAFE_INTEGER); // 最小安全整数 (-(2^53 - 1))
// 数值运算
console.log(0.1 + 0.2); // 输出: 0.30000000000000004 (浮点数精度问题)
console.log((0.1 * 10 + 0.2 * 10) / 10); // 输出: 0.3 (解决方法)
// 数值转换
console.log(Number("123")); // 输出: 123
console.log(Number("123.45")); // 输出: 123.45
console.log(Number("123abc")); // 输出: NaN
console.log(parseInt("123.45")); // 输出: 123 (解析整数部分)
console.log(parseFloat("123.45abc")); // 输出: 123.45 (解析浮点数部分)
5. String 类型
String 类型用于表示文本数据,由零个或多个 16 位 Unicode 字符组成。
// String 类型示例
let singleQuotes = 'Hello, world!'; // 单引号字符串
let doubleQuotes = "Hello, world!"; // 双引号字符串
let backticks = `Hello, world!`; // 模板字符串(ES6)
let emptyString = ""; // 空字符串
console.log(singleQuotes, doubleQuotes, backticks, emptyString);
console.log(typeof singleQuotes); // 输出: "string"
// 转义字符
let escaped = "First line\nSecond line\tTabbed\"Quote\'Quote\\Backslash";
console.log(escaped);
/*
输出:
First line
Second line Tabbed"Quote'Quote\Backslash
*/
// 字符串长度
let str = "JavaScript";
console.log(str.length); // 输出: 10
// 访问字符
console.log(str[0]); // 输出: "J" (ES5+方式)
console.log(str.charAt(1)); // 输出: "a" (传统方式)
console.log(str.charCodeAt(0)); // 输出: 74 (J的Unicode编码)
// 字符串不可变性
str[0] = "X"; // 尝试修改字符串
console.log(str); // 输出: "JavaScript" (字符串不会被修改)
// 字符串连接
let concat1 = "Hello" + " " + "World"; // 使用+运算符
let concat2 = "Hello".concat(" ", "World"); // 使用concat方法
console.log(concat1, concat2);
// 模板字符串(ES6)
let name = "Alice";
let age = 25;
let template = `My name is ${name} and I'm ${age} years old.`;
console.log(template); // 输出: My name is Alice and I'm 25 years old.
// 多行字符串
let multiLine = `This is
a multi-line
string.`;
console.log(multiLine);
// 常用字符串方法
let sample = "Hello, JavaScript!";
console.log(sample.toUpperCase()); // 输出: "HELLO, JAVASCRIPT!"
console.log(sample.toLowerCase()); // 输出: "hello, javascript!"
console.log(sample.indexOf("Java")); // 输出: 7 (首次出现位置)
console.log(sample.lastIndexOf("a")); // 输出: 9 (最后出现位置)
console.log(sample.includes("Script")); // 输出: true (是否包含)
console.log(sample.startsWith("Hello")); // 输出: true (是否以...开头)
console.log(sample.endsWith("!")); // 输出: true (是否以...结尾)
console.log(sample.slice(7, 10)); // 输出: "Jav" (提取子串)
console.log(sample.substring(7, 10)); // 输出: "Jav" (类似slice)
console.log(sample.substr(7, 3)); // 输出: "Jav" (从位置7开始取3个字符)
console.log(sample.split(", ")); // 输出: ["Hello", "JavaScript!"] (分割字符串)
console.log(sample.replace("JavaScript", "World")); // 输出: "Hello, World!" (替换)
console.log(" trim me ".trim()); // 输出: "trim me" (去除两端空格)
类型检测与转换
// 类型检测
console.log(typeof undefined); // 输出: "undefined"
console.log(typeof null); // 输出: "object" (历史遗留问题)
console.log(typeof true); // 输出: "boolean"
console.log(typeof 42); // 输出: "number"
console.log(typeof "hello"); // 输出: "string"
// 更精确的类型检测
console.log(Object.prototype.toString.call(undefined)); // [object Undefined]
console.log(Object.prototype.toString.call(null)); // [object Null]
console.log(Object.prototype.toString.call(true)); // [object Boolean]
console.log(Object.prototype.toString.call(42)); // [object Number]
console.log(Object.prototype.toString.call("hello")); // [object String]
// 类型转换
// 转换为字符串
console.log(String(123)); // 输出: "123"
console.log(String(true)); // 输出: "true"
console.log(String(null)); // 输出: "null"
console.log(String(undefined)); // 输出: "undefined"
console.log(123 + ""); // 输出: "123" (隐式转换)
// 转换为数字
console.log(Number("123")); // 输出: 123
console.log(Number("123.45")); // 输出: 123.45
console.log(Number(true)); // 输出: 1
console.log(Number(false)); // 输出: 0
console.log(Number(null)); // 输出: 0
console.log(Number(undefined)); // 输出: NaN
console.log(+"123"); // 输出: 123 (使用+运算符隐式转换)
// 转换为布尔值
console.log(Boolean(0)); // 输出: false
console.log(Boolean(1)); // 输出: true
console.log(Boolean("")); // 输出: false
console.log(Boolean("hello")); // 输出: true
console.log(Boolean(null)); // 输出: false
console.log(Boolean(undefined)); // 输出: false
console.log(Boolean({})); // 输出: true
console.log(!!"hello"); // 输出: true (使用!!运算符隐式转换)
以上内容涵盖了 JavaScript 的基本数据类型及其相关操作,包括 Undefined、Null、Boolean、Number 和 String 类型。理解这些数据类型及其行为对于编写健壮的 JavaScript 代码至关重要。
案例案例
下面我将通过几个实际开发中常见的场景,展示不同数据类型的具体应用。
1. 表单验证(Boolean/String 应用)
/**
* 用户注册表单验证
* 应用: Boolean判断, String处理
*/
function validateRegistrationForm(user) {
// 检查用户名
if (!user.username || user.username.trim().length < 6) {
return {
valid: false,
message: "用户名不能为空且至少6个字符"
};
}
// 检查密码强度 (至少8位,包含字母和数字)
const passwordRegex = /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$/;
if (!passwordRegex.test(user.password)) {
return {
valid: false,
message: "密码必须至少8位且包含字母和数字"
};
}
// 检查邮箱格式
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(user.email)) {
return {
valid: false,
message: "请输入有效的邮箱地址"
};
}
// 检查同意条款 (Boolean类型检查)
if (!user.agreeTerms) {
return {
valid: false,
message: "必须同意服务条款"
};
}
// 所有验证通过
return {
valid: true,
message: "验证通过"
};
}
// 测试用例
const testUser1 = {
username: "john_doe",
password: "abc12345",
email: "john@example.com",
agreeTerms: true
};
const testUser2 = {
username: "joe",
password: "password",
email: "invalid-email",
agreeTerms: false
};
console.log(validateRegistrationForm(testUser1));
// 输出: { valid: true, message: "验证通过" }
console.log(validateRegistrationForm(testUser2));
// 输出: { valid: false, message: "用户名不能为空且至少6个字符" }
2. 购物车计算(Number 应用)
/**
* 购物车结算功能
* 应用: Number精确计算, 浮点数处理
*/
class ShoppingCart {
constructor() {
this.items = [];
this.taxRate = 0.08; // 8%税率
}
addItem(name, price, quantity = 1) {
this.items.push({
name,
// 使用Number()确保价格为数字类型
price: Number(price),
quantity: Number(quantity)
});
}
// 计算小计 (避免浮点数精度问题)
calculateSubtotal() {
// 使用整数运算避免浮点问题 (以分为单位)
const subtotalInCents = this.items.reduce((total, item) => {
return total + Math.round(item.price * 100) * item.quantity;
}, 0);
// 转换回元
return subtotalInCents / 100;
}
// 计算税费
calculateTax() {
const subtotal = this.calculateSubtotal();
// 使用toFixed解决显示问题,但返回数字类型
return parseFloat((subtotal * this.taxRate).toFixed(2));
}
// 计算总价
calculateTotal() {
const subtotal = this.calculateSubtotal();
const tax = this.calculateTax();
return parseFloat((subtotal + tax).toFixed(2));
}
// 生成账单摘要
generateReceipt() {
console.log("=== 购物清单 ===");
this.items.forEach(item => {
console.log(`${item.name} x${item.quantity}: ¥${(item.price * item.quantity).toFixed(2)}`);
});
console.log("\n=== 结算信息 ===");
console.log(`小计: ¥${this.calculateSubtotal().toFixed(2)}`);
console.log(`税费 (${this.taxRate * 100}%): ¥${this.calculateTax().toFixed(2)}`);
console.log(`总计: ¥${this.calculateTotal().toFixed(2)}`);
}
}
// 使用示例
const cart = new ShoppingCart();
cart.addItem("iPhone 13", 5999.99);
cart.addItem("AirPods Pro", 1499, 2);
cart.addItem("USB-C 充电线", 79.5, 3);
cart.generateReceipt();
/*
输出:
=== 购物清单 ===
iPhone 13 x1: ¥5999.99
AirPods Pro x2: ¥2998.00
USB-C 充电线 x3: ¥238.50
=== 结算信息 ===
小计: ¥9236.49
税费 (8%): ¥738.92
总计: ¥9975.41
*/
3. 用户配置处理(Null/Undefined 应用)
/**
* 应用配置管理器
* 应用: Null/Undefined检查, 默认值处理
*/
class AppConfig {
constructor(defaultConfig = {}) {
// 合并默认配置
this.config = {
theme: "light",
fontSize: 14,
notifications: true,
...defaultConfig
};
}
// 获取配置项 (处理可能的undefined)
get(key) {
// 使用nullish coalescing操作符(??)提供默认值
return this.config[key] ?? `[配置项${key}不存在]`;
}
// 设置配置项 (处理null值)
set(key, value) {
if (value === null) {
console.warn(`配置项${key}不能设置为null,操作已忽略`);
return false;
}
// 使用optional chaining(?.)安全访问
if (this.config?.[key] !== undefined || this.config.hasOwnProperty(key)) {
this.config[key] = value;
return true;
}
console.warn(`配置项${key}不存在,无法设置`);
return false;
}
// 重置为默认值 (处理undefined情况)
reset(key) {
const defaults = {
theme: "light",
fontSize: 14,
notifications: true
};
if (key === undefined) {
// 重置所有配置
this.config = { ...defaults };
return true;
}
if (defaults[key] !== undefined) {
this.config[key] = defaults[key];
return true;
}
return false;
}
// 显示当前配置
display() {
console.log("当前配置:");
Object.entries(this.config).forEach(([key, value]) => {
console.log(`${key}: ${value} (${typeof value})`);
});
}
}
// 使用示例
const config = new AppConfig({
theme: "dark",
language: "zh-CN"
});
config.display();
/*
输出:
当前配置:
theme: dark (string)
fontSize: 14 (number)
notifications: true (boolean)
language: zh-CN (string)
*/
console.log(config.get("theme")); // 输出: dark
console.log(config.get("nonExistent")); // 输出: [配置项nonExistent不存在]
config.set("fontSize", 16);
config.set("nonExistent", "value"); // 警告: 配置项nonExistent不存在,无法设置
config.set("theme", null); // 警告: 配置项theme不能设置为null,操作已忽略
config.display();
/*
输出:
当前配置:
theme: dark (string)
fontSize: 16 (number)
notifications: true (boolean)
language: zh-CN (string)
*/
config.reset("fontSize");
config.display();
/*
输出:
当前配置:
theme: dark (string)
fontSize: 14 (number)
notifications: true (boolean)
language: zh-CN (string)
*/
4. 字符串模板生成器(String 高级应用)
/**
* 动态HTML模板生成器
* 应用: 模板字符串, 多行字符串, 字符串插值
*/
class TemplateEngine {
constructor() {
this.templates = {};
}
// 注册模板
registerTemplate(name, templateString) {
this.templates[name] = templateString;
}
// 渲染模板
render(templateName, data = {}) {
if (!this.templates[templateName]) {
throw new Error(`模板 ${templateName} 未注册`);
}
let result = this.templates[templateName];
// 替换所有 ${prop} 为实际值
result = result.replace(/\$\{(\w+)\}/g, (match, prop) => {
// 处理嵌套属性 (如 user.name)
const value = prop.split('.').reduce((obj, key) => {
return obj?.[key];
}, data);
// 如果值为undefined或null,替换为空字符串
return value !== undefined && value !== null ? value : '';
});
return result;
}
}
// 使用示例
const engine = new TemplateEngine();
// 注册用户卡片模板
engine.registerTemplate('userCard', `
<div class="user-card">
<h2>${'user.name'}</h2>
<p class="email">${'user.email'}</p>
${'user.avatar'
? `<img src="${'user.avatar'}" alt="${'user.name'}'s avatar" class="avatar">`
: '<div class="default-avatar">No Avatar</div>'
}
<ul class="stats">
<li>Posts: ${'stats.posts'}</li>
<li>Followers: ${'stats.followers'}</li>
<li>Following: ${'stats.following'}</li>
</ul>
${'user.isAdmin'
? '<button class="admin-btn">Admin Panel</button>'
: '<button class="message-btn">Send Message</button>'
}
</div>
`);
// 准备数据
const userData = {
user: {
name: "Alice Johnson",
email: "alice@example.com",
avatar: "https://example.com/avatars/alice.jpg",
isAdmin: true
},
stats: {
posts: 42,
followers: 1024,
following: 64
}
};
// 渲染模板
const renderedHtml = engine.render('userCard', userData);
console.log(renderedHtml);
/*
输出:
<div class="user-card">
<h2>Alice Johnson</h2>
<p class="email">alice@example.com</p>
<img src="https://example.com/avatars/alice.jpg" alt="Alice Johnson's avatar" class="avatar">
<ul class="stats">
<li>Posts: 42</li>
<li>Followers: 1024</li>
<li>Following: 64</li>
</ul>
<button class="admin-btn">Admin Panel</button>
</div>
*/
5. 数字格式化工具(Number 高级应用)
/**
* 数字格式化工具集
* 应用: Number处理, 数学运算, 国际化
*/
class NumberFormatter {
// 格式化货币
static formatCurrency(value, currency = 'CNY', locale = 'zh-CN') {
return new Intl.NumberFormat(locale, {
style: 'currency',
currency: currency,
minimumFractionDigits: 2,
maximumFractionDigits: 2
}).format(Number(value));
}
// 格式化百分比
static formatPercent(value, decimalPlaces = 2) {
const percent = Number(value) * 100;
return percent.toFixed(decimalPlaces) + '%';
}
// 格式化大数字 (如 1K, 1M)
static abbreviateNumber(value, decimalPlaces = 1) {
const num = Number(value);
if (num < 1000) return num.toString();
const units = ['K', 'M', 'B', 'T'];
const exp = Math.floor(Math.log10(num) / 3);
const abbreviated = num / Math.pow(1000, exp);
// 处理1.0K等情况,去掉小数部分
if (abbreviated % 1 === 0 && decimalPlaces > 0) {
return abbreviated.toFixed(0) + units[exp - 1];
}
return abbreviated.toFixed(decimalPlaces) + units[exp - 1];
}
// 生成随机数范围
static randomInRange(min, max, integer = true) {
const minNum = Number(min);
const maxNum = Number(max);
const random = Math.random() * (maxNum - minNum) + minNum;
return integer ? Math.floor(random) : random;
}
// 安全计算 (避免浮点数精度问题)
static safeCalculate(operation, ...numbers) {
// 将所有数字转换为整数(放大10000倍)
const scaledNumbers = numbers.map(num =>
Math.round(Number(num) * 10000)
);
let result;
switch (operation) {
case '+':
result = scaledNumbers.reduce((sum, num) => sum + num, 0);
break;
case '-':
result = scaledNumbers.slice(1).reduce(
(diff, num) => diff - num, scaledNumbers[0]
);
break;
case '*':
result = scaledNumbers.reduce((product, num) => product * num, 1);
// 因为每个数都放大了10000倍,乘法需要额外处理
result = result / Math.pow(10000, scaledNumbers.length - 1);
break;
case '/':
result = scaledNumbers.slice(1).reduce(
(quotient, num) => quotient / num, scaledNumbers[0]
);
// 除法也需要调整
result = result * Math.pow(10000, scaledNumbers.length - 2);
break;
default:
throw new Error('不支持的运算类型');
}
// 缩小回原始比例
return result / 10000;
}
}
// 使用示例
console.log(NumberFormatter.formatCurrency(1234.567)); // 输出: ¥1,234.57
console.log(NumberFormatter.formatCurrency(1234.567, 'USD', 'en-US')); // 输出: $1,234.57
console.log(NumberFormatter.formatPercent(0.12345)); // 输出: 12.35%
console.log(NumberFormatter.formatPercent(0.12345, 1)); // 输出: 12.3%
console.log(NumberFormatter.abbreviateNumber(1234)); // 输出: 1.2K
console.log(NumberFormatter.abbreviateNumber(1234567)); // 输出: 1.2M
console.log(NumberFormatter.abbreviateNumber(1000)); // 输出: 1K
console.log(NumberFormatter.randomInRange(1, 10)); // 输出: 1到10的随机整数
console.log(NumberFormatter.randomInRange(0.1, 1.0, false)); // 输出: 0.1到1.0的随机浮点数
// 解决浮点数精度问题
console.log(0.1 + 0.2); // 输出: 0.30000000000000004
console.log(NumberFormatter.safeCalculate('+', 0.1, 0.2)); // 输出: 0.3
console.log(1.1 * 1.1); // 输出: 1.2100000000000002
console.log(NumberFormatter.safeCalculate('*', 1.1, 1.1)); // 输出: 1.21
这些案例展示了 JavaScript 数据类型在实际开发中的应用场景,涵盖了表单验证、购物车计算、配置管理、模板生成和数字格式化等常见需求。每个案例都充分利用了不同数据类型的特性,并处理了边界情况和潜在问题。