1. JavaScript常用库函数概览
作为一名前端开发者,我每天都要和各种JavaScript库函数打交道。这些看似简单的工具函数,实际上构成了我们日常开发的基石。记得刚入行时,我总是习惯性地重复造轮子,直到有次看到同事用一行Array.prototype.map()就解决了我写了20多行循环代码的问题,才真正意识到掌握这些基础库函数的重要性。
JavaScript的标准库函数主要分布在以下几个核心对象上:
- Array:处理数组操作的利器
- String:字符串处理的瑞士军刀
- Object:对象操作的基础工具
- Math:数学计算的宝库
- Date:日期时间处理的必备工具
- JSON:数据格式转换的标准方案
这些函数之所以被称为"常用",是因为它们:
- 被ECMAScript标准定义,所有JavaScript环境都支持
- 解决了80%的日常开发需求
- 经过多年优化,性能有保障
- 具有一致的API设计,学习成本低
2. 数组操作:从forEach到reduce
2.1 遍历类函数实战
forEach是我最早接触的数组方法,但直到踩过几次坑才真正理解它:
const arr = [1, 2, 3]; arr.forEach((item, index) => { console.log(`第${index}个元素是${item}`); if(index === 1) arr.push(4); // 不会影响本次遍历 });注意:forEach无法用break中断,使用return也只是跳过当前项而非退出循环
map和forEach最大的区别在于它会返回新数组:
const users = [{name:'Alice'}, {name:'Bob'}]; const names = users.map(user => { return user.name.toUpperCase(); // 必须显式return }); console.log(names); // ['ALICE', 'BOB']2.2 查找筛选三剑客
filter、find和findIndex构成了数据筛选的黄金组合:
const products = [ {id:1, stock:0}, {id:2, stock:5}, {id:3, stock:2} ]; // filter返回所有匹配项 const available = products.filter(p => p.stock > 0); console.log(available.length); // 2 // find只返回第一个匹配项 const firstAvailable = products.find(p => p.stock > 0); console.log(firstAvailable.id); // 2 // findIndex返回索引 const index = products.findIndex(p => p.stock > 3); console.log(index); // 12.3 reduce的进阶用法
reduce是最强大的数组方法,但也是最难掌握的。这个统计词频的例子展示了它的威力:
const words = ['hello', 'world', 'hello']; const wordCount = words.reduce((acc, word) => { acc[word] = (acc[word] || 0) + 1; return acc; }, {}); console.log(wordCount); // {hello: 2, world: 1}更实用的一个场景是表单数据收集:
const formFields = [ {name: 'username', value: 'Alice'}, {name: 'age', value: '25'} ]; const formData = formFields.reduce((data, field) => { data[field.name] = field.value; return data; }, {}); console.log(formData); // {username: "Alice", age: "25"}3. 字符串处理的精髓
3.1 模板字符串的妙用
自从ES6引入模板字符串,字符串拼接变得无比优雅:
const user = {name: 'Bob', age: 30}; console.log(`用户${user.name}今年${user.age}岁`); // 更高级的用法:带标签的模板字符串 function highlight(strings, ...values) { return strings.reduce((result, str, i) => { return `${result}${str}<mark>${values[i] || ''}</mark>`; }, ''); } const message = highlight`重要通知:${user.name}的账户存在异常`; console.log(message); // "重要通知:<mark>Bob</mark>的账户存在异常"3.2 正则相关方法
match、search、replace这三个方法与正则表达式配合使用威力巨大:
const text = "订单号:123-4567-890"; // 提取订单号 const orderNo = text.match(/\d{3}-\d{4}-\d{3}/)[0]; console.log(orderNo); // "123-4567-890" // 安全替换 const masked = text.replace(/\d/g, '*'); console.log(masked); // "订单号:***-****-***"3.3 实用工具方法
padStart和padEnd在处理格式化输出时特别有用:
// 时间格式化 const minutes = '5'; const seconds = '7'; console.log(`${minutes.padStart(2,'0')}:${seconds.padEnd(2,'0')}`); // "05:70" // 表格对齐 const products = [ {name: 'iPhone', price: 999}, {name: 'MacBook Pro', price: 1999} ]; products.forEach(p => { console.log(`${p.name.padEnd(15)} | ${p.price.toString().padStart(6)}$`); }); // iPhone | 999$ // MacBook Pro | 1999$4. 对象操作的现代方式
4.1 对象属性遍历
Object.keys、Object.values和Object.entries构成了对象遍历的三驾马车:
const user = { name: 'Alice', age: 25, [Symbol('id')]: 123 // 符号属性 }; // 获取可枚举属性 console.log(Object.keys(user)); // ["name", "age"] console.log(Object.values(user)); // ["Alice", 25] console.log(Object.entries(user)); // [["name", "Alice"], ["age", 25]] // 包含不可枚举属性 console.log(Object.getOwnPropertyNames(user)); // ["name", "age"] console.log(Object.getOwnPropertySymbols(user)); // [Symbol(id)]4.2 对象合并与克隆
Object.assign和展开运算符(...)是现代JavaScript对象复制的首选方式:
const defaults = {theme: 'light', fontSize: 14}; const userSettings = {fontSize: 16}; // 浅合并 const settings = Object.assign({}, defaults, userSettings); console.log(settings); // {theme: "light", fontSize: 16} // 使用展开运算符更直观 const newSettings = {...defaults, ...userSettings}; // 深克隆方案 const deepClone = obj => JSON.parse(JSON.stringify(obj)); const nestedObj = {a: {b: 1}}; const cloned = deepClone(nestedObj); console.log(cloned.a === nestedObj.a); // false4.3 属性描述符操作
Object.defineProperty让我们可以精细控制对象属性:
const book = {title: 'JavaScript高级编程'}; Object.defineProperty(book, 'price', { value: 99, writable: false, // 不可写 enumerable: true, // 可枚举 configurable: false // 不可删除 }); console.log(book.price); // 99 book.price = 100; // 静默失败(严格模式下会报错) console.log(book.price); // 995. 数学与日期处理
5.1 Math的实用方法
Math对象提供了各种数学计算工具:
// 随机数生成 function getRandom(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } console.log(getRandom(1, 10)); // 1到10的随机整数 // 数值处理 console.log(Math.trunc(3.14)); // 3 (直接取整) console.log(Math.round(3.5)); // 4 (四舍五入) console.log(Math.ceil(3.1)); // 4 (向上取整) console.log(Math.floor(3.9)); // 3 (向下取整) // 最大值/最小值 console.log(Math.max(...[1, 2, 3])); // 3 console.log(Math.min(1, 2, 3)); // 15.2 日期处理技巧
JavaScript的Date对象虽然饱受诟病,但掌握后也能得心应手:
// 创建日期 const now = new Date(); const specificDate = new Date(2023, 5, 15); // 注意月份是0-based // 格式化输出 console.log(now.toLocaleDateString('zh-CN')); // "2023/6/15" console.log(now.toISOString()); // "2023-06-15T08:00:00.000Z" // 日期计算 const tomorrow = new Date(now); tomorrow.setDate(now.getDate() + 1); // 日期差值(毫秒) const diff = tomorrow - now; console.log(diff / (1000 * 60 * 60 * 24)); // 1(天)6. JSON数据转换
6.1 基本序列化与反序列化
JSON.stringify和JSON.parse是处理JSON数据的黄金搭档:
const user = { name: 'Alice', age: 25, lastLogin: new Date(), sayHi() { console.log('Hi!'); } }; const jsonStr = JSON.stringify(user); console.log(jsonStr); // {"name":"Alice","age":25,"lastLogin":"2023-06-15T08:00:00.000Z"} // 注意:函数被忽略了,日期被转为字符串 const revivedUser = JSON.parse(jsonStr); console.log(revivedUser.lastLogin); // 字符串而非Date对象6.2 高级序列化控制
通过replacer和reviver函数可以实现精细控制:
const product = { id: 123, name: 'Laptop', price: 999, secretKey: 'abc123' }; // 序列化时排除secretKey const json = JSON.stringify(product, (key, value) => { return key === 'secretKey' ? undefined : value; }); console.log(json); // {"id":123,"name":"Laptop","price":999} // 反序列化时恢复Date对象 const str = '{"date":"2023-06-15T08:00:00.000Z"}'; const data = JSON.parse(str, (key, value) => { if (typeof value === 'string' && /^\d{4}-\d{2}-\d{2}T/.test(value)) { return new Date(value); } return value; }); console.log(data.date instanceof Date); // true7. 其他实用工具函数
7.1 类型判断全家桶
JavaScript的类型判断一直是个痛点,这些方法可以帮我们:
// 基本类型判断 console.log(typeof 42); // "number" console.log(typeof 'text'); // "string" console.log(typeof true); // "boolean" console.log(typeof undefined); // "undefined" console.log(typeof Symbol()); // "symbol" console.log(typeof null); // "object" (历史遗留问题) // 更准确的类型判断 console.log(Object.prototype.toString.call([])); // "[object Array]" console.log(Object.prototype.toString.call(new Date())); // "[object Date]" // 现代API console.log(Array.isArray([])); // true console.log(Number.isNaN(NaN)); // true7.2 全局函数解析
parseInt和parseFloat虽然常用但容易踩坑:
console.log(parseInt('08')); // 8 (ES5+) console.log(parseInt('08', 10)); // 8 (明确指定基数更安全) console.log(parseInt('0x10')); // 16 (十六进制) console.log(parseFloat('3.14.15')); // 3.14 (遇到第二个小数点停止)isNaN和Number.isNaN的区别:
console.log(isNaN('text')); // true (先尝试转换为数字) console.log(Number.isNaN('text')); // false (严格判断)7.3 异步编程基础
虽然Promise和async/await已经成为主流,但回调风格的API仍大量存在:
// 传统回调 function fetchData(callback) { setTimeout(() => { callback(null, {data: 'some data'}); }, 1000); } // 转换为Promise function fetchDataAsync() { return new Promise((resolve, reject) => { fetchData((err, result) => { if (err) reject(err); else resolve(result); }); }); } // 使用async/await async function process() { try { const data = await fetchDataAsync(); console.log(data); } catch (error) { console.error(error); } }8. 实战中的经验与陷阱
8.1 性能优化要点
数组方法的选择会影响性能:
const bigArray = new Array(1000000).fill(1); console.time('for'); let sum = 0; for (let i = 0; i < bigArray.length; i++) { sum += bigArray[i]; } console.timeEnd('for'); // ~5ms console.time('forEach'); sum = 0; bigArray.forEach(n => sum += n); console.timeEnd('forEach'); // ~15ms console.time('reduce'); sum = bigArray.reduce((acc, n) => acc + n, 0); console.timeEnd('reduce'); // ~20ms提示:在超大数据集上,传统for循环通常性能最好
8.2 常见陷阱规避
- 数组的
sort陷阱:
const nums = [10, 5, 80, 1]; nums.sort(); console.log(nums); // [1, 10, 5, 80] (按字符串排序) // 正确做法 nums.sort((a, b) => a - b); console.log(nums); // [1, 5, 10, 80]- 浮点数精度问题:
console.log(0.1 + 0.2 === 0.3); // false console.log(0.1 + 0.2); // 0.30000000000000004 // 解决方案 function isEqual(a, b, epsilon = 1e-10) { return Math.abs(a - b) < epsilon; }this绑定问题:
const counter = { count: 0, increment: function() { setInterval(function() { this.count++; // 这里的this指向window/undefined console.log(this.count); }, 1000); } }; // 解决方案1:箭头函数 const counter2 = { count: 0, increment: function() { setInterval(() => { this.count++; console.log(this.count); }, 1000); } }; // 解决方案2:bind const counter3 = { count: 0, increment: function() { setInterval(function() { this.count++; console.log(this.count); }.bind(this), 1000); } };8.3 现代JavaScript新特性
- 可选链操作符(?.):
const user = { profile: { name: 'Alice' } }; // 传统写法 const name = user && user.profile && user.profile.name; // 现代写法 const name = user?.profile?.name;- 空值合并运算符(??):
const config = { timeout: 0 }; // 传统写法 const timeout = config.timeout !== undefined ? config.timeout : 3000; // 现代写法 const timeout = config.timeout ?? 3000; console.log(timeout); // 0 (因为0不是null/undefined)- Object.hasOwn替代in操作符:
const obj = { a: 1 }; console.log('a' in obj); // true (包括原型链) console.log(Object.hasOwn(obj, 'a')); // true (仅自身属性) console.log(Object.hasOwn(obj, 'toString')); // false