原生 JavaScript 方法实战指南
平时开发中经常用到各种 JS 方法,但有些细节老是记不住,踩过不少坑。整理了这份笔记,记录一下常用方法的实际用法和注意事项。
数组方法
map() - 最常用的数组转换
日常开发中用得最多的方法之一,把数组每个元素都处理一下:
javascript
// 最基本的用法
const numbers = [1, 2, 3, 4];
const doubled = numbers.map((n) => n * 2);
console.log(doubled); // [2, 4, 6, 8]
// 处理对象数组,这个场景很常见
const users = [
{ id: 1, name: 'John', age: 25 },
{ id: 2, name: 'Jane', age: 30 },
];
// 提取特定字段
const names = users.map((user) => user.name);
console.log(names); // ['John', 'Jane']
// 添加新属性
const usersWithStatus = users.map((user) => ({
...user,
isAdult: user.age >= 18,
}));
注意事项:
map
不会改变原数组- 如果不
return
任何值,结果数组中对应位置就是undefined
- 稀疏数组的空位会被跳过
javascript
// 这个坑我踩过
const arr = [1, 2, 3];
const result = arr.map((n) => {
console.log(n); // 会打印,但没有 return
});
console.log(result); // [undefined, undefined, undefined]
filter() - 筛选数据
经常用来过滤数组数据:
javascript
// 基本用法
const ages = [15, 18, 21, 25, 30];
const adults = ages.filter((age) => age >= 18);
console.log(adults); // [18, 21, 25, 30]
// 过滤对象数组
const products = [
{ name: '苹果', price: 5, inStock: true },
{ name: '香蕉', price: 3, inStock: false },
{ name: '橘子', price: 4, inStock: true },
];
const availableProducts = products.filter((p) => p.inStock && p.price > 3);
console.log(availableProducts); // [{ name: '苹果', price: 5, inStock: true }, { name: '橘子', price: 4, inStock: true }]
// 去重(结合 indexOf)
const duplicates = [1, 2, 2, 3, 3, 4];
const unique = duplicates.filter(
(item, index) => duplicates.indexOf(item) === index
);
console.log(unique); // [1, 2, 3, 4]
find() 和 findIndex()
find()
找到第一个符合条件的元素:
javascript
const users = [
{ id: 1, name: 'John', email: 'john@example.com' },
{ id: 2, name: 'Jane', email: 'jane@example.com' },
];
// 根据 ID 查找用户
const user = users.find((u) => u.id === 2);
console.log(user); // { id: 2, name: 'Jane', email: 'jane@example.com' }
// 找不到返回 undefined
const notFound = users.find((u) => u.id === 999);
console.log(notFound); // undefined
// findIndex() 返回索引
const userIndex = users.findIndex((u) => u.name === 'Jane');
console.log(userIndex); // 1
reduce() - 最强大也是最容易搞混的
刚开始学的时候老是搞不懂,多练几次就好了:
javascript
// 最简单的累加
const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((acc, curr) => acc + curr, 0);
console.log(sum); // 15
// 找最大值
const max = numbers.reduce((acc, curr) => Math.max(acc, curr));
console.log(max); // 5
// 统计出现次数
const fruits = ['apple', 'banana', 'apple', 'orange', 'banana'];
const count = fruits.reduce((acc, fruit) => {
acc[fruit] = (acc[fruit] || 0) + 1;
return acc;
}, {});
console.log(count); // { apple: 2, banana: 2, orange: 1 }
// 数组转对象(经常用到)
const users = [
{ id: 1, name: 'John' },
{ id: 2, name: 'Jane' },
];
const userMap = users.reduce((acc, user) => {
acc[user.id] = user;
return acc;
}, {});
console.log(userMap); // { 1: { id: 1, name: 'John' }, 2: { id: 2, name: 'Jane' } }
forEach() vs for 循环
forEach
写起来简洁,但有些限制:
javascript
const numbers = [1, 2, 3, 4, 5];
// 基本用法
numbers.forEach((num, index) => {
console.log(`索引 ${index}: ${num}`);
});
// 注意:forEach 不能用 break 或 continue
// 这样会报错
numbers.forEach((num) => {
if (num === 3) {
// break; // SyntaxError!
return; // 这个只会跳过当前迭代,类似 continue
}
console.log(num);
});
// 如果需要中途退出,用 for 循环或 some/every
for (let num of numbers) {
if (num === 3) break; // 这样才行
console.log(num);
}
some() 和 every()
检查数组条件的好帮手:
javascript
const ages = [16, 18, 20, 22];
// some(): 有一个满足就返回 true
const hasAdult = ages.some((age) => age >= 18);
console.log(hasAdult); // true
// every(): 全部满足才返回 true
const allAdults = ages.every((age) => age >= 18);
console.log(allAdults); // false
// 实际应用:表单验证
const formFields = [
{ name: 'email', value: 'test@example.com', required: true },
{ name: 'name', value: '', required: true },
{ name: 'phone', value: '123456', required: false },
];
const hasEmptyRequired = formFields.some(
(field) => field.required && !field.value
);
console.log(hasEmptyRequired); // true (name 字段为空)
字符串方法
split() 和 join()
处理字符串分割和拼接:
javascript
// 基本分割
const str = 'apple,banana,orange';
const fruits = str.split(',');
console.log(fruits); // ['apple', 'banana', 'orange']
// 限制分割数量
const limited = str.split(',', 2);
console.log(limited); // ['apple', 'banana']
// 处理路径
const path = '/user/profile/edit';
const segments = path.split('/').filter(Boolean); // 过滤空字符串
console.log(segments); // ['user', 'profile', 'edit']
// join() 拼接回去
const joined = fruits.join(' | ');
console.log(joined); // 'apple | banana | orange'
// 常用技巧:数组去重后拼接
const tags = ['js', 'react', 'js', 'vue', 'react'];
const uniqueTags = [...new Set(tags)].join(', ');
console.log(uniqueTags); // 'js, react, vue'
includes() 和 indexOf()
字符串搜索方法:
javascript
const text = 'Hello JavaScript World';
// includes() 返回布尔值,更语义化
console.log(text.includes('JavaScript')); // true
console.log(text.includes('Python')); // false
// indexOf() 返回位置,-1 表示没找到
console.log(text.indexOf('JavaScript')); // 6
console.log(text.indexOf('Python')); // -1
// 从指定位置开始搜索
console.log(text.indexOf('o', 5)); // 15 (从位置5开始找)
// 实际应用:检查文件类型
const fileName = 'document.pdf';
const isPdf = fileName.includes('.pdf');
const isImage = ['.jpg', '.png', '.gif'].some((ext) => fileName.includes(ext));
slice()、substring()、substr()
字符串截取,这三个经常搞混:
javascript
const str = 'Hello JavaScript';
// slice() - 最常用,支持负数
console.log(str.slice(0, 5)); // 'Hello'
console.log(str.slice(6)); // 'JavaScript'
console.log(str.slice(-10)); // 'JavaScript' (从倒数第10位开始)
console.log(str.slice(-10, -6)); // 'Java' (倒数第10到第6位)
// substring() - 不支持负数,负数当0处理
console.log(str.substring(0, 5)); // 'Hello'
console.log(str.substring(-5)); // 'Hello JavaScript' (负数当0处理)
// substr() - 已废弃,不建议使用
console.log(str.substr(6, 4)); // 'Java'
推荐只用 slice()
,功能最全面。
trim()、padStart()、padEnd()
处理字符串空白和填充:
javascript
// 去除空白
const messy = ' hello world ';
console.log(messy.trim()); // 'hello world'
console.log(messy.trimStart()); // 'hello world '
console.log(messy.trimEnd()); // ' hello world'
// 填充字符串 - 格式化数字时很有用
const num = 5;
console.log(num.toString().padStart(3, '0')); // '005'
console.log('5'.padEnd(10, '-')); // '5---------'
// 实际应用:格式化时间
function formatTime(hours, minutes) {
const h = hours.toString().padStart(2, '0');
const m = minutes.toString().padStart(2, '0');
return `${h}:${m}`;
}
console.log(formatTime(9, 5)); // '09:05'
对象方法
Object.keys()、Object.values()、Object.entries()
遍历对象的好帮手:
javascript
const user = {
id: 1,
name: 'John',
email: 'john@example.com',
age: 25,
};
// 获取所有键
const keys = Object.keys(user);
console.log(keys); // ['id', 'name', 'email', 'age']
// 获取所有值
const values = Object.values(user);
console.log(values); // [1, 'John', 'john@example.com', 25]
// 获取键值对数组
const entries = Object.entries(user);
console.log(entries); // [['id', 1], ['name', 'John'], ['email', 'john@example.com'], ['age', 25]]
// 实际应用:对象转换
const transformed = Object.entries(user).reduce((acc, [key, value]) => {
acc[key.toUpperCase()] = value;
return acc;
}, {});
console.log(transformed); // { ID: 1, NAME: 'John', EMAIL: 'john@example.com', AGE: 25 }
Object.assign() 和展开运算符
对象合并,现在更多用展开运算符:
javascript
const defaults = { theme: 'light', fontSize: 14 };
const userSettings = { fontSize: 16, color: 'blue' };
// Object.assign() 方式
const merged1 = Object.assign({}, defaults, userSettings);
console.log(merged1); // { theme: 'light', fontSize: 16, color: 'blue' }
// 展开运算符(推荐)
const merged2 = { ...defaults, ...userSettings };
console.log(merged2); // { theme: 'light', fontSize: 16, color: 'blue' }
// 注意:都是浅拷贝
const user = {
name: 'John',
settings: { theme: 'dark' },
};
const copy = { ...user };
copy.settings.theme = 'light'; // 会影响原对象
console.log(user.settings.theme); // 'light'
hasOwnProperty() 和 in 运算符
检查对象属性:
javascript
const user = { name: 'John', age: 25 };
// hasOwnProperty() 只检查自有属性
console.log(user.hasOwnProperty('name')); // true
console.log(user.hasOwnProperty('toString')); // false
// in 运算符检查包括继承的属性
console.log('name' in user); // true
console.log('toString' in user); // true
// 更安全的写法(避免对象重写 hasOwnProperty)
console.log(Object.prototype.hasOwnProperty.call(user, 'name')); // true
// 实际应用:安全地访问对象属性
function getValue(obj, key, defaultValue) {
return obj.hasOwnProperty(key) ? obj[key] : defaultValue;
}
类型判断方法
typeof 和 instanceof
基本类型判断:
javascript
// typeof 判断基本类型
console.log(typeof 'hello'); // 'string'
console.log(typeof 42); // 'number'
console.log(typeof true); // 'boolean'
console.log(typeof undefined); // 'undefined'
console.log(typeof null); // 'object' (这是个历史bug)
console.log(typeof {}); // 'object'
console.log(typeof []); // 'object'
console.log(typeof function () {}); // 'function'
// instanceof 判断对象类型
console.log([] instanceof Array); // true
console.log({} instanceof Object); // true
console.log(new Date() instanceof Date); // true
// 更准确的类型判断
function getType(value) {
return Object.prototype.toString.call(value).slice(8, -1).toLowerCase();
}
console.log(getType([])); // 'array'
console.log(getType({})); // 'object'
console.log(getType(null)); // 'null'
console.log(getType(new Date())); // 'date'
Array.isArray()
专门判断数组:
javascript
// 最可靠的数组判断方法
console.log(Array.isArray([])); // true
console.log(Array.isArray({})); // false
console.log(Array.isArray('hello')); // false
// 为什么不用 typeof
const arr = [];
console.log(typeof arr); // 'object' - 不准确
// 为什么不用 instanceof
// 在不同 iframe 中创建的数组,instanceof 可能返回 false
数学方法
Math 对象常用方法
javascript
// 基本数学运算
console.log(Math.max(1, 5, 3)); // 5
console.log(Math.min(1, 5, 3)); // 1
console.log(Math.abs(-5)); // 5
// 四舍五入相关
console.log(Math.round(4.6)); // 5
console.log(Math.floor(4.9)); // 4 (向下取整)
console.log(Math.ceil(4.1)); // 5 (向上取整)
// 随机数
console.log(Math.random()); // 0-1 之间的随机数
// 实用的随机数函数
function randomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
console.log(randomInt(1, 10)); // 1-10 之间的随机整数
// 数组随机元素
function randomChoice(arr) {
return arr[Math.floor(Math.random() * arr.length)];
}
const colors = ['red', 'blue', 'green'];
console.log(randomChoice(colors));
数字格式化
javascript
const num = 1234.5678;
// toFixed() 保留小数位
console.log(num.toFixed(2)); // '1234.57'
// 注意:toFixed 返回字符串,且会四舍五入
console.log(typeof num.toFixed(2)); // 'string'
// parseInt() 和 parseFloat()
console.log(parseInt('123.45')); // 123
console.log(parseFloat('123.45')); // 123.45
console.log(parseInt('123abc')); // 123 (会忽略后面的字符)
console.log(parseInt('abc123')); // NaN
// 数字验证
function isValidNumber(value) {
return !isNaN(value) && isFinite(value);
}
console.log(isValidNumber('123')); // true
console.log(isValidNumber('abc')); // false
console.log(isValidNumber(Infinity)); // false
日期方法
Date 对象基本操作
javascript
// 创建日期
const now = new Date();
const specificDate = new Date('2023-12-25');
const timestamp = new Date(1640995200000);
// 获取日期部分
console.log(now.getFullYear()); // 2025
console.log(now.getMonth()); // 8 (注意:0-11)
console.log(now.getDate()); // 26
console.log(now.getDay()); // 4 (0=周日, 1=周一...)
// 获取时间部分
console.log(now.getHours()); // 14
console.log(now.getMinutes()); // 30
console.log(now.getSeconds()); // 45
// 时间戳
console.log(now.getTime()); // 毫秒时间戳
console.log(Date.now()); // 当前时间戳(静态方法)
// 实用函数:格式化日期
function formatDate(date) {
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const day = date.getDate().toString().padStart(2, '0');
return `${year}-${month}-${day}`;
}
console.log(formatDate(new Date())); // '2025-09-26'
日期计算
javascript
const now = new Date();
// 计算几天前/后
function addDays(date, days) {
const result = new Date(date);
result.setDate(result.getDate() + days);
return result;
}
const tomorrow = addDays(now, 1);
const lastWeek = addDays(now, -7);
// 计算两个日期的差值
function daysBetween(date1, date2) {
const diffTime = Math.abs(date2 - date1);
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
return diffDays;
}
const start = new Date('2025-09-01');
const end = new Date('2025-09-26');
console.log(daysBetween(start, end)); // 25
常见坑点和注意事项
1. 数组方法的返回值
javascript
const arr = [1, 2, 3];
// 这些方法不改变原数组,返回新数组
console.log(arr.map((x) => x * 2)); // [2, 4, 6]
console.log(arr.filter((x) => x > 1)); // [2, 3]
console.log(arr.slice(1)); // [2, 3]
console.log(arr); // [1, 2, 3] 原数组没变
// 这些方法会改变原数组
arr.push(4); // 返回新长度
arr.pop(); // 返回删除的元素
arr.sort(); // 返回排序后的数组(原数组也变了)
arr.reverse(); // 返回翻转后的数组(原数组也变了)
2. 类型转换陷阱
javascript
// 字符串数字比较
console.log('2' > '10'); // true (字符串比较)
console.log(2 > 10); // false (数字比较)
// 数组转字符串
console.log([1, 2, 3].toString()); // '1,2,3'
console.log([1, 2, 3].join('')); // '123'
// 对象转字符串
console.log({}.toString()); // '[object Object]'
console.log(JSON.stringify({})); // '{}'
3. this 指向问题
javascript
const obj = {
name: 'Test',
greet: function () {
console.log(this.name);
},
delayedGreet: function () {
setTimeout(function () {
console.log(this.name); // undefined (this 指向 window)
}, 1000);
},
delayedGreetArrow: function () {
setTimeout(() => {
console.log(this.name); // 'Test' (箭头函数保持 this)
}, 1000);
},
};
实际项目中的应用
数据处理管道
javascript
// 处理 API 返回的用户数据
const rawUsers = [
{
id: 1,
name: 'john doe',
email: 'JOHN@EXAMPLE.COM',
age: '25',
active: 'true',
},
{
id: 2,
name: 'jane smith',
email: 'jane@example.com',
age: '30',
active: 'false',
},
];
const processedUsers = rawUsers
.filter((user) => user.active === 'true') // 只要活跃用户
.map((user) => ({
...user,
name: user.name
.split(' ')
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(' '), // 首字母大写
email: user.email.toLowerCase(), // 邮箱小写
age: parseInt(user.age), // 转为数字
active: user.active === 'true', // 转为布尔值
}));
console.log(processedUsers);
// [{ id: 1, name: 'John Doe', email: 'john@example.com', age: 25, active: true }]
表单验证
javascript
function validateForm(formData) {
const errors = {};
// 检查必填字段
const requiredFields = ['name', 'email', 'password'];
requiredFields.forEach((field) => {
if (!formData[field] || !formData[field].toString().trim()) {
errors[field] = `${field} 是必填项`;
}
});
// 邮箱格式验证
if (formData.email && !formData.email.includes('@')) {
errors.email = '邮箱格式不正确';
}
// 密码长度验证
if (formData.password && formData.password.length < 6) {
errors.password = '密码长度不能少于6位';
}
return {
isValid: Object.keys(errors).length === 0,
errors,
};
}
const result = validateForm({
name: 'John',
email: 'john@example.com',
password: '12345',
});
console.log(result); // { isValid: false, errors: { password: '密码长度不能少于6位' } }
数据统计
javascript
// 统计订单数据
const orders = [
{
id: 1,
product: 'iPhone',
price: 999,
status: 'completed',
date: '2025-09-01',
},
{ id: 2, product: 'iPad', price: 599, status: 'pending', date: '2025-09-02' },
{
id: 3,
product: 'iPhone',
price: 999,
status: 'completed',
date: '2025-09-03',
},
];
// 总收入(已完成订单)
const totalRevenue = orders
.filter((order) => order.status === 'completed')
.reduce((sum, order) => sum + order.price, 0);
// 产品销量统计
const productStats = orders
.filter((order) => order.status === 'completed')
.reduce((stats, order) => {
stats[order.product] = (stats[order.product] || 0) + 1;
return stats;
}, {});
// 按状态分组
const ordersByStatus = orders.reduce((groups, order) => {
if (!groups[order.status]) {
groups[order.status] = [];
}
groups[order.status].push(order);
return groups;
}, {});
console.log('总收入:', totalRevenue); // 1998
console.log('产品统计:', productStats); // { iPhone: 2 }
console.log('状态分组:', ordersByStatus);
性能优化小贴士
1. 避免不必要的数组操作
javascript
// ❌ 不好的写法 - 多次遍历
const data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const result = data
.filter((x) => x > 5)
.map((x) => x * 2)
.filter((x) => x < 20);
// ✅ 更好的写法 - 单次遍历
const betterResult = data.reduce((acc, x) => {
if (x > 5) {
const doubled = x * 2;
if (doubled < 20) {
acc.push(doubled);
}
}
return acc;
}, []);
2. 合理使用缓存
javascript
// 缓存复杂计算结果
const memoize = (fn) => {
const cache = new Map();
return (...args) => {
const key = JSON.stringify(args);
if (cache.has(key)) {
return cache.get(key);
}
const result = fn(...args);
cache.set(key, result);
return result;
};
};
const expensiveCalculation = memoize((n) => {
console.log('计算中...');
return n * n * n;
});
console.log(expensiveCalculation(5)); // 计算中... 125
console.log(expensiveCalculation(5)); // 125 (从缓存读取)
整理于 2025 年 9 月,实际项目踩坑总结