目录
- 一、String&Number处理
- 1.indexOf()
- 2. includes()
- 3. slice(start, end)
- 4. split(separator)
- 5. substring(start, end)
- 6. toUpperCase()/toLowerCase()
- 7. trim()
- 8. match(regexp)
- 9. replace(search, replacement)
- 10. charAt(index)
- 11. repeat(count)
- 12. startsWith()/endsWith()
- 13. codePointAt()
- 14. normalize()
- 15. 比较时间
- 16. 格式化money
- 17. 生成随机ID(可以加入当前时间的毫秒数)
- 18. 生成随机HEX颜色值
- 19. URLSearchParams API(现代浏览器推荐)
- 20. 生成星级
- 21. Number()
- 22. parseInt()/parseFloat()
- 23. toFixed(digits)
- 24. 取整方法
- 25. 极值与随机数
- 26. Number.isNaN()
- 27. Number.isInteger()
- 28. 浮点数运算修正
- 29. toLocaleString()
- 30. 零填充
- 31. 转number类型
- 32. 精确小数
- 33. 取最大值最小值
- 34. 生成范围随机数
- 二、布尔值
- 三、数组操作
- 四、对象处理
- 五、函数技巧
- 六、DOM操作
一、String&Number处理
1.indexOf()
返回子串首次出现的位置索引(区分大小写),未找到返回-1
'hello'.indexOf('l'); // 2
2. includes()
判断是否包含子串(ES6新增,返回布尔值)
'abc'.includes('b'); // true
3. slice(start, end)
截取指定区间(支持负数索引):
'javascript'.slice(0,4); // 'java'
4. split(separator)
按分隔符拆分为数组:
'a,b,c'.split(','); // ['a','b','c']
5. substring(start, end)
类似slice但不支持负数,自动交换参数
'substring'.substring(2,5); // 'bst'
6. toUpperCase()/toLowerCase()
全字母大小写转换:
'Hello'.toLowerCase(); // 'hello'
7. trim()
去除首尾空格(ES5+):
' text '.trim(); // 'text'
8. match(regexp)
正则匹配返回结果数组:
'2023-07-15'.match(/\d+/g); // ['2023','07','15']
9. replace(search, replacement)
替换子串(支持正则)
'foo bar'.replace('bar','baz'); // 'foo baz'
10. charAt(index)
获取指定位置字符
'abc'.charAt(1); // 'b'
11. repeat(count)
重复字符串(ES6)
'na'.repeat(2); // 'nana'
12. startsWith()/endsWith()
检测开头/结尾(ES6)
'file.txt'.endsWith('.txt'); // true
13. codePointAt()
获取字符的Unicode码点(支持emoji)
'😊'.codePointAt(0); // 128522
14. normalize()
标准化Unicode字符(如去除变音符号)
'\u0041\u030A'.normalize(); // 'Å'
注意事项
- 以上所有方法均不改变原字符串(字符串不可变)
- 涉及正则的方法如match()、replace()可配合/g标志全局匹配
- 优先使用ES6+方法(如includes()替代indexOf() !== -1)
15. 比较时间
const time1 = "2018-03-24 07:00:00";
const time2 = "2018-03-24 07:00:01";
time1 < time2;// true
16. 格式化money
const ThousandNum = num => num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")
ThousandNum(1000000);//'1,000,000'
17. 生成随机ID(可以加入当前时间的毫秒数)
const RandomId = len => Math.random().toString(36).substr(3, len);
const id = RandomId(10);//7f0w91zid
18. 生成随机HEX颜色值
const RandomColor = () => "#" + Math.floor(Math.random() * 0xffffff).toString(
const color = RandomColor();//#5003992
19. URLSearchParams API(现代浏览器推荐)
// 获取单个参数
const params = new URLSearchParams(window.location.search);
const value = params.get('参数名'); // 返回指定参数值或null:ml-citation{ref="2,5" data="citationList"}
// 获取所有参数(转换为对象)
const allParams = Object.fromEntries(params.entries());:ml-citation{ref="6,10" data="citationList"}
20. 生成星级
const StartScore = rate => "★★★★★☆☆☆☆☆".slice(5 - rate, 10 - rate);
const start = StartScore(3);//'★★★☆☆'
21. Number()
将字符串/布尔值等转换为数字:
Number("123") // 123
Number(true) // 1
22. parseInt()/parseFloat()
解析字符串为整数/浮点数:
parseInt("10.5px") // 10
parseFloat("3.14") // 3.14
23. toFixed(digits)
保留指定位数小数(四舍五入):
3.1415.toFixed(2) // "3.14"
24. 取整方法
Math.ceil(3.1) // 4(向上取整)
Math.floor(3.9) // 3(向下取整)
Math.round(3.5) // 4(四舍五入)
const num1 = ~~ 1.19; //1
const num2 = 2.29 | 0; //2
const num3 = 3.09 >> 0; //3
25. 极值与随机数
Math.max(1, 3, 2) // 3
Math.random() // [0,1)随机数
26. Number.isNaN()
严格检测NaN(比全局isNaN更安全):
Number.isNaN("NaN") // false
27. Number.isInteger()
检测是否为整数:
Number.isInteger(5.0) // true
28. 浮点数运算修正
使用库或放大为整数运算
function add(a, b) {
const multiplier = 10 ** Math.max(
a.toString().split('.')[1]?.length || 0,
b.toString().split('.')[1]?.length || 0
);
return (a * multiplier + b * multiplier) / multiplier;
}
29. toLocaleString()
本地化千分位格式化:
1234567.89.toLocaleString() // "1,234,567.89"
注意事项
- 浮点数计算建议使用 toFixed() 显示但避免用于计算
- 大整数需用 BigInt 类型处理(如 9007199254740992n)
- 货币计算推荐使用 decimal.js 等专业库
30. 零填充
const FillZero = (num, len) => num.toString().padStart(len, "0");
const num = FillZero(1234, 7);
console.log(num) //0001234
31. 转number类型
const num1 = +null;//0
const num2 = +"";//0
const num3 = +false;//0
const num4 = +"169";//169
const timestamp = +new Date("2018-03-24");//1521849600000
//注意:仅对以上有效。
32. 精确小数
const RoundNum = (num, decimal) => Math.round(num * 10 ** decimal) / 10 ** decimal;
const num = RoundNum(1.2345, 2);// num => 1.23
33. 取最大值最小值
const arr = [5, 2, 9, 1, 7];
const max = Math.max(...arr); // 9
const min = Math.min(...arr); // 1
34. 生成范围随机数
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
const num = getRandomInt(min, max)//7
二、布尔值
1. 双重取反(!!)强制转换
使用!!value可将任意值转为布尔类型
console.log(!!"hello"); // true
console.log(!!0); // false
原理:
第一次!将值转为布尔并取反。
第二次!还原为真实布尔值。
2. 六种假值(Falsy Values)
只有以下六种值会转为false
false, 0, "", null, undefined, NaN // 其他值均为 true
3. 短路运算
// 条件执行
isReady && startProcess(); // 仅当 isReady=true 时执行
// 默认值设置
const name = inputName || "匿名用户";
4. 布尔值比较陷阱
避免直接比较布尔对象,优先使用字面量true/false,强制类型意识、避免隐式转换、代码语义透明化。
const boolObj = new Boolean(false);
console.log(boolObj == false); // true (值相等)
console.log(boolObj === false); // false (类型不同)
5. 字符串检测
"hello.js".endsWith(".js"); // true
"Title:".startsWith("Title"); // true
6. 数组包含检测
[1, 2, 3].includes(2); // true
7. 条件语句优化
// 优于 if(value !== null && value !== undefined)
if (value) { ... }
8. 类型安全验证
function validate(input) {
return typeof input === "boolean" ? input : false;
}
9. 确定数据类型
可确定的类型:undefined、null、string、number、boolean、array、object、symbol、date、regexp、function、asyncfunction、arguments、set、map、weakset、weakmap。
function DataType(tgt, type) {
const dataType = Object.prototype.toString.call(tgt).replace(/\[object (\w+)\]/, "$1").toLowerCase();
return type ? dataType === type : dataType;
}
DataType("test"); // "string"
DataType(20220314); // "number"
DataType(true); // "boolean"
DataType([], "array"); // true
DataType({}, "array"); // false
10. 检查是否为数组且是否为空
const arr = [];
const flag = Array.isArray(arr) && !arr.length;// flag => true
11.满足条件时执行
const flagA = true;
const flagB = false;
(flagA || flagB) && Func();
(flagA || !flagB) && Func();
flagA && flagB && Func();
flagA && !flagB && Func();
//非假执行
const flag = false; // undefined、null、""、0、false、NaN
!flag && Func();
//数组不为空时执行
const arr = [0, 1, 2];
arr.length && Func();
//对象不为空时执行
const obj = { a: 0, b: 1, c: 2 };
Object.keys(obj).length && Func();
三、数组操作
1. 克隆数组
//浅拷贝方法4种(仅复制第一层)
const newArr = [...originalArr];//扩展运算符
const newArr = Array.from(originalArr);//Array.from()
const newArr = originalArr.slice();//slice()
const newArr = [].concat(originalArr);//concat()
//深拷贝方法两种(嵌套结构完全复制)
const newArr = JSON.parse(JSON.stringify(originalArr));//JSON 序列化(限制:无法处理函数/Symbol等特殊类型)
//递归深拷贝
function deepClone(arr) {
return arr.map(item => Array.isArray(item) ? deepClone(item) : item);
}
2. 合并数组
const arr1 = [0, 1, 2];
const arr2 = [3, 4, 5];
const arr = [...arr1, ...arr2];//[0, 1, 2, 3, 4, 5]
3. 数组去重
const arr = [...new Set([0, 1, 1, null, null])];// [0, 1, null]
4. 混淆数组
const arr = [0, 1, 2, 3, 4, 5].slice().sort(() => Math.random() - .5);//[1, 5, 3, 2, 4, 0]
5. 清空数组
const arr = [0, 1, 2];
arr.length = 0;//[]
6. 截断数组
const arr = [0, 1, 2];
arr.length = 2;// [0, 1]
7. 交换数值
let a = 0;
let b = 1;
[a, b] = [b, a];//a b => 1 0
8. 过滤空值
空值:undefined,null,””,0,false,NaN
const arr = [undefined, null, "", 0, false, NaN, 1, 2].filter(Boolean);//[1,2]
9. 数组插入元素
//数组开头插入元素
let arr = [1, 2];
arr.unshift(0);
arr = [0].concat(arr);
arr = [0, ...arr];
//数组尾部插入元素
let arr = [0, 1];
arr.push(2);
arr.concat(2);
arr[arr.length] = 2;
arr = [...arr, 2];
//提示:高频操作建议使用 splice,比如websocket。需要保持原数组不变时优先选择扩展运算符方案。
10. 数组扁平化处理
//直接展开指定深度的嵌套数组(默认深度为1)
[1, [2, [3]]].flat(2); // [1, 2, 3]
//展开任意深度的嵌套数组
[1, [2, [3, [4]]]].flat(Infinity); // [1, 2, 3, 4]
//对象数组处理,支持对象属性的递归展开
function flattenObject(arr, key) {
return arr.flatMap(item =>
item[key] ? [item, ...flattenObject(item[key], key)] : [item]
);
}
//利用 reduce 进行迭代展开
const flatten = arr =>
arr.reduce((a, b) => a.concat(Array.isArray(b) ? flatten(b) : b), []);
//结合 some 和扩展运算符逐层展开
function flatten(arr) {
while (arr.some(Array.isArray)) {
arr = [].concat(...arr);
}
return arr;
}
11. 数组解构
//解构多层嵌套数组
const [color1, [color2, color2]] = ['red', ['green', 'blue']];
// color1='red', color2='green', color3='blue'
//解构数组成员别名
const arr = [0, 1, 2];
const { 0: a, 1: b, 2: c } = arr;//a b c => 0 1 2
//使用空逗号跳过不需要的元素
const [first, , third] = ['a', 'b', 'c']; // first='a', third='c'
//用 ... 捕获剩余元素:
const [head, ...tail] = [1, 2, 3]; // head=1, tail=[2,3]
// 解构不存在的数组元素时使用默认值
const colors = ['red'];
const [firstColor, secondColor = 'green'] = colors;
console.log(secondColor); // 'green'(因为colors[1]未定义)
//自定义迭代器实现类数组解构
const obj = {
*:ml-search[Symbol.iterator] {
yield 1; yield 2
}
};
const [a, b] = obj; // 成功解构
12. 生成固定长度数组
//生成并填充固定长度的数组
const arr1 = Array(5).fill(0); // [0, 0, 0, 0, 0]
//生成指定长度的数组
const arr2 = [...new Array(3).keys()];//[0, 1, 2]
const arr3 = Array.from({length: 3}, (_, i) => i + 1); // [1, 2, 3]
const arr4 = [...Array(4)].map((_, i) => i * 2); // [0, 2, 4, 6]
const randomArr = Array(5).fill().map(() => Math.random());//[0.8482335137420847, 0.14620341731276887, 0.13255749533610994, 0.09141648742263153, 0.5173177360096568]
//生成二维矩阵
const matrix = Array(3).fill().map(() => Array(3).fill(0));//[[0, 0, 0],[0, 0, 0],[0, 0, 0]]
13. 随机选取数组元素
const arr = [1, 2, 3, 4, 5];
const randomItem = arr[Math.floor(Math.random() * arr.length)];
//封装为函数
function getRandomElement(array) {
return array[Math.floor(Math.random() * array.length)];
}
14. 数组分组
//需要自定义分组逻辑时,通过累加器构建分组对象。
const groupBy = (arr, key) =>
arr.reduce((acc, obj) => {
const groupKey = obj[key];
(acc[groupKey] || (acc[groupKey] = [])).push(obj);
return acc;
}, {});
//支持非字符串键值,使用 Map 存储分组结果
const map = new Map();
arr.forEach(item => {
const key = item.department;
if (!map.has(key)) map.set(key, []);
map.get(key).push(item);
});
//Object.groupBy
const result = Object.groupBy(users, ({age}) => age);
//Map.groupBy
const result = Map.groupBy(users, ({gender}) => gender);
四、对象处理
1. 对象常用方法
分类 | 方法名 | 描述 | 示例 |
---|---|---|---|
属性操作 | Object.keys(obj) | 返回对象自身可枚举属性的键名数组 | Object.keys({a:1,b:2}) → ['a','b'] |
Object.values(obj) | 返回对象自身可枚举属性的值数组 | Object.values({a:1,b:2}) → [1,2] | |
Object.entries(obj) | 返回对象自身可枚举属性的键值对数组 | Object.entries({a:1}) → [['a',1]] | |
obj.hasOwnProperty(key) | 检查对象是否包含特定自身属性(不包括原型链) | {a:1}.hasOwnProperty('a') → true | |
Object.hasOwn(obj,key) | 更安全的属性检查方法(ES2022) | Object.hasOwn({a:1},'a') → true | |
对象操作 | Object.assign(target,...sources) | 合并多个对象(浅拷贝) | Object.assign({a:1},{b:2}) → {a:1,b:2} |
Object.defineProperty(obj,prop,descriptor) | 定义/修改对象属性特性 | Object.defineProperty(obj,'x',{value:1,writable:false}) | |
Object.freeze(obj) | 冻结对象,使其不可修改 | Object.freeze(obj) 后修改obj会静默失败或报错 | |
Object.seal(obj) | 密封对象,防止添加/删除属性 | Object.seal(obj) 后不能添加/删除属性 | |
原型相关 | Object.create(proto) | 使用指定原型创建新对象 | const child = Object.create(parent) |
Object.getPrototypeOf(obj) | 获取对象的原型 | Object.getPrototypeOf(array) → Array.prototype | |
Object.setPrototypeOf(obj,proto) | 设置对象的原型(性能敏感,慎用) | Object.setPrototypeOf(obj,newProto) | |
比较与转换 | Object.is(val1,val2) | 严格比较两个值是否相同(比===更准确) | Object.is(NaN,NaN) → true |
JSON.stringify(obj) | 将对象转换为JSON字符串 | JSON.stringify({x:1}) → '{"x":1}' | |
JSON.parse(jsonStr) | 将JSON字符串解析为对象 | JSON.parse('{"a":1}') → {a:1} | |
ES6+新增 | Object.fromEntries(iterable) | 将键值对数组转换为对象 | Object.fromEntries([['a',1]]) → {a:1} |
展开运算符 ... | 对象浅拷贝/合并的现代语法 | const copy = {...obj} const merged = {...obj1,...obj2} | |
可选链 ?. | 安全访问嵌套属性(ES2020) | obj?.a?.b 当a不存在时返回undefined而不报错 | |
空值合并 ?? | 仅在null/undefined时使用默认值(ES2020) | const val = obj.a ?? 10 |
2. 合并对象
//扩展运算符(ES6+推荐)【高性能浅拷贝】
const merged = {...obj1, ...obj2};
// 同名属性后者覆盖前者
//Object.assign()(ES5+)【中性能浅拷贝】
const merged = Object.assign({}, obj1, obj2);
// 第一个参数是目标对象(会修改它)
//手动深拷贝合并【低性能深拷贝】
function deepMerge(target, source) {
for (let key in source) {
if (source[key] instanceof Object) {
target[key] = deepMerge(target[key] || {}, source[key]);
} else {
target[key] = source[key];
}
}
return target;
}
//Lodash库的_.merge()【中性能深拷贝】
const merged = _.merge({}, obj1, obj2);
// 自动处理嵌套对象合并
//JSON序列化(简易深拷贝)【最低性能深拷贝】
const merged = JSON.parse(JSON.stringify({
...obj1,
...obj2
}));
3. 删除对象无用属性
//使用对象解构,创建新对象删除
const obj = { a: 1, b: 2, c: 3, d: 4 };
// 删除单个属性
const { b, ...rest } = obj;
console.log(rest); // { a: 1, c: 3, d: 4 }
// 删除多个属性
const { a, d, ...newObj } = obj;
console.log(newObj); // { b: 2, c: 3 }
//使用 delete 操作符直接删除
const obj = { a: 1, b: 2, c: 3 };
// 删除单个属性
delete obj.b;
console.log(obj); // { a: 1, c: 3 }
// 批量删除属性
['a', 'c'].forEach(prop => delete obj[prop]);
console.log(obj); // {}
//使用 Lodash 库的 omit 方法
// 需要先安装 lodash
import _ from 'lodash';
const obj = { a: 1, b: 2, c: 3 };
const cleaned = _.omit(obj, ['a', 'c']);
console.log(cleaned); // { b: 2 }
4. 对象解构
// 基本解构
const person = {
name: 'Alice',
age: 25,
profession: 'Developer'
};
const { name, age } = person;
console.log(name); // 'Alice'
console.log(age); // 25
//嵌套解构
const user = {
id: 1,
name: 'John',
address: {
street: '123 Main St',
city: 'New York',
country: 'USA'
}
};
// 解构嵌套的address对象
const { address: { city, country } } = user;
console.log(city); // 'New York'
console.log(country); // 'USA'
//属性默认值
const { name, age, country = 'USA' } = person;
console.log(country); // 'USA' (当person.country不存在时)
//展开符
const { name, ...rest } = person;
console.log(rest); // { age: 25, profession: 'Developer' }
五、函数技巧
1. 箭头函数简化写法
// 传统函数
function add(a, b) {
return a + b;
}
// 箭头函数简化
const add = (a, b) => a + b;
// 单参数可省略括号
const square = x => x * x;
2. 默认参数值
function greet(name = 'Guest') {
console.log(`Hello, ${name}!`);
}
greet(); // Hello, Guest!
greet('Alice'); // Hello, Alice!
3. 剩余参数(Rest Parameters)
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
console.log(sum(1, 2, 3)); // 6
4. 解构参数
function printUser({name, age, city = 'Unknown'}) {
console.log(`${name}, ${age} years old from ${city}`);
}
printUser({name: 'Bob', age: 30}); // Bob, 30 years old from Unknown
5. 高阶函数
// 函数作为参数
const numbers = [1, 2, 3];
const doubled = numbers.map(num => num * 2); // [2, 4, 6]
// 函数返回函数
const multiplier = factor => number => number * factor;
const double = multiplier(2);
console.log(double(5)); // 10
6. 立即调用函数表达式(IIFE)
(function() {
console.log('This runs immediately');
})();
// 带参数的IIFE
(function(name) {
console.log(`Hello, ${name}!`);
})('Alice');
7. 闭包应用
function createCounter() {
let count = 0;
return {
increment: () => ++count,
getCount: () => count
};
}
const counter = createCounter();
counter.increment();
console.log(counter.getCount()); // 1
8. this绑定控制
const person = {
name: 'Alice',
greet: function() {
console.log(`Hello, ${this.name}!`);
}
};
// 使用bind固定this
const greet = person.greet.bind(person);
setTimeout(greet, 100); // Hello, Alice!
// 箭头函数继承this
const person2 = {
name: 'Bob',
greet: function() {
setTimeout(() => {
console.log(`Hello, ${this.name}!`);
}, 100);
}
};
person2.greet(); // Hello, Bob!
9. 异步函数处理
// Promise链式调用
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));
// async/await简化
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}
}
10. 函数缓存(Memoization)
function 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 expensiveCalc = n => {
console.log('Calculating...');
return n * 2;
};
const memoizedCalc = memoize(expensiveCalc);
console.log(memoizedCalc(5)); // Calculating... 10
console.log(memoizedCalc(5)); // 10 (从缓存读取)
11. 函数节流与防抖
// 节流
function throttle(fn, limit) {
let inThrottle;
return function(...args) {
if (!inThrottle) {
fn.apply(this, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
// 防抖
function debounce(fn, delay) {
let timer;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
};
}
12. 函数柯里化
函数柯里化(Currying)是一种将多参数函数转换为一系列单参数函数的技术。柯里化后的函数会逐步接收参数,直到接收到所有参数后才执行原函数。
基本概念
// 普通函数
function add(a, b, c) {
return a + b + c;
}
// 柯里化后的函数
function curriedAdd(a) {
return function(b) {
return function(c) {
return a + b + c;
};
};
}
// 调用方式对比
add(1, 2, 3); // 6
curriedAdd(1)(2)(3); // 6
JavaScript 中柯里化的使用频率
柯里化在 JavaScript 中的使用情况:
-
使用频率中等:
- 函数式编程库(如 Lodash、Ramda)中常见
- React 高阶组件等特定场景有应用
- 日常业务代码中使用相对较少
-
适用场景:
- 需要部分应用参数的情况
- 需要延迟执行的场景
- 函数组合(function composition)时
- 需要创建专用函数时
柯里化的优势
- 参数复用
// 创建专用函数
const add5 = curriedAdd(5);
const add5And10 = add5(10);
add5And10(3); // 18
- 延迟执行
// 直到收到所有参数才执行
const log = level => message => `[${level}] ${message}`;
const errorLog = log('ERROR');
errorLog('Something went wrong'); // "[ERROR] Something went wrong"
- 函数组合更灵活
const compose = (...fns) => x => fns.reduceRight((v, f) => f(v), x);
const double = x => x * 2;
const increment = x => x + 1;
// 柯里化使组合更清晰
const transform = compose(double, increment);
transform(5); // 12 (先increment再double)
实际应用示例
- 事件处理
// 普通处理
button.addEventListener('click', (e) => handleClick(id, e));
// 柯里化处理
const handleClick = id => e => { /*...*/ };
button.addEventListener('click', handleClick(id));
- API请求
// 创建专用请求函数
const apiRequest = baseUrl => endpoint => params =>
fetch(`${baseUrl}/${endpoint}`, { ...params });
const myApi = apiRequest('https://api.example.com');
const getUser = myApi('users');
getUser({ id: 123 });
- React高阶组件
// 柯里化高阶组件
const withStyles = styles => Component => props => (
<Component {...props} styles={styles} />
);
// 使用
const StyledButton = withStyles(buttonStyles)(Button);
自动柯里化实现
function curry(fn) {
return function curried(...args) {
// 参数足够时执行原函数
if (args.length >= fn.length) {
return fn.apply(this, args);
}
// 参数不足时返回新函数继续收集参数
else {
return function(...moreArgs) {
return curried.apply(this, args.concat(moreArgs));
};
}
};
}
// 使用示例
const curriedAdd = curry((a, b, c) => a + b + c);
curriedAdd(1)(2)(3); // 6
curriedAdd(1, 2)(3); // 6
curriedAdd(1, 2, 3); // 6
何时使用柯里化
✅ 推荐使用:
- 需要创建多个相似函数(参数部分相同)
- 函数组合场景
- 需要明确参数传递顺序时
❌ 不推荐使用:
- 性能敏感场景(柯里化有额外函数调用开销)
- 参数数量不固定的函数
- 团队不熟悉函数式编程时
柯里化是一种强大的技术,但应该根据实际需求谨慎使用,避免过度工程化。
六、DOM操作
1. 元素选择技巧
基础选择方法
// 通过ID获取
const element = document.getElementById('myId');
// 通过类名获取(返回HTMLCollection)
const elements = document.getElementsByClassName('myClass');
// 通过标签名获取(返回HTMLCollection)
const divs = document.getElementsByTagName('div');
// 通过CSS选择器获取(返回第一个匹配元素)
const item = document.querySelector('.item');
// 通过CSS选择器获取所有匹配元素(返回NodeList)
const items = document.querySelectorAll('.item');
现代选择技巧
// 在特定元素内查询
const container = document.getElementById('container');
const innerItems = container.querySelectorAll('.item');
// 获取表单元素
const formInputs = document.forms['myForm'].elements;
2. 元素内容操作
// 获取/设置文本内容
element.textContent = '新内容';
const text = element.textContent;
// 获取/设置HTML内容
element.innerHTML = '<strong>加粗文本</strong>';
// 获取/设置value(表单元素)
inputElement.value = '新值';
// 安全插入HTML(避免XSS)
element.insertAdjacentHTML('beforeend', '<p>安全插入</p>');
3. 元素属性操作
// 获取/设置标准属性
const href = link.getAttribute('href');
link.setAttribute('href', 'https://newurl.com');
// 获取/设置data属性
const user = element.dataset.userId;
// 切换类名
element.classList.add('active');
element.classList.remove('active');
element.classList.toggle('active');
element.classList.contains('active'); // 检查
// 直接修改样式
element.style.color = 'red';
element.style.backgroundColor = '#fff';
4. 元素创建与删除
// 创建新元素
const newDiv = document.createElement('div');
// 添加子元素
parentElement.appendChild(newDiv);
// 插入到特定位置
parentElement.insertBefore(newDiv, referenceElement);
// 克隆元素
const clonedElement = element.cloneNode(true); // 深度克隆
// 移除元素
parentElement.removeChild(element);
element.remove(); // 现代方法
5. 事件处理技巧
基本事件绑定
// 传统方式
element.onclick = function(e) { /*...*/ };
// 现代方式(可添加多个监听器)
element.addEventListener('click', function(e) {
console.log(e.target); // 触发事件的元素
});
常用事件技巧
// 事件委托
document.getElementById('list').addEventListener('click', function(e) {
if(e.target.classList.contains('item')) {
// 处理.item元素的点击
}
});
// 一次性事件
element.addEventListener('click', function handler(e) {
// 处理逻辑
e.currentTarget.removeEventListener('click', handler);
});
// 防抖事件
element.addEventListener('input', debounce(function(e) {
// 输入停止后才执行
}, 300));
6. 页面结构遍历
// 获取父元素
const parent = element.parentNode;
// 获取子元素
const firstChild = element.firstElementChild;
const children = element.children; // HTMLCollection
// 获取兄弟元素
const nextSibling = element.nextElementSibling;
const prevSibling = element.previousElementSibling;
7. 表单操作技巧
// 获取表单数据
const formData = new FormData(formElement);
const username = formData.get('username');
// 监听表单提交
form.addEventListener('submit', function(e) {
e.preventDefault(); // 阻止默认提交
// 处理表单数据
});
// 表单验证
inputElement.addEventListener('input', function() {
if(!inputElement.checkValidity()) {
// 显示错误信息
}
});
8. 性能优化技巧
// 批量DOM修改使用文档片段
const fragment = document.createDocumentFragment();
for(let i = 0; i < 100; i++) {
const item = document.createElement('div');
fragment.appendChild(item);
}
container.appendChild(fragment);
// 避免强制同步布局(布局抖动)
// 不好的做法:
for(let i = 0; i < items.length; i++) {
items[i].style.width = box.offsetWidth + 'px';
}
// 好的做法:
const width = box.offsetWidth;
for(let i = 0; i < items.length; i++) {
items[i].style.width = width + 'px';
}
9. 现代DOM API
// IntersectionObserver(检测元素可见性)
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if(entry.isIntersecting) {
// 元素进入视口
}
});
});
observer.observe(element);
// MutationObserver(监听DOM变化)
const observer = new MutationObserver((mutations) => {
mutations.forEach(mutation => {
// 处理DOM变化
});
});
observer.observe(element, { attributes: true, childList: true });
// 元素尺寸观察
const resizeObserver = new ResizeObserver(entries => {
for(let entry of entries) {
console.log('新尺寸:', entry.contentRect);
}
});
resizeObserver.observe(element);
10. 实用代码片段
// 检查元素是否在视口中
function isInViewport(element) {
const rect = element.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
);
}
// 平滑滚动到元素
function smoothScrollTo(element) {
element.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
// 获取计算样式
function getStyle(element, property) {
return window.getComputedStyle(element).getPropertyValue(property);
}
11.过滤 XSS
function FilterXss(content, allowedTags = ['b', 'i', 'em', 'strong']) {
const temp = document.createElement("div");
temp.textContent = content;
let html = temp.innerHTML;
// 保留白名单标签
if (allowedTags.length) {
const tagsRegex = new RegExp(
`</?(?!(${allowedTags.join('|')})\\w+[^>]*>`,
'gi'
);
html = html.replace(tagsRegex, '');
}
// 移除危险属性
html = html.replace(
/( on\w+|href|src|style)=["'][^"']*["']/gi,
(match, attr) => {
if (attr === 'href' || attr === 'src') {
if (/javascript:/gi.test(match)) return '';
if (attr === 'src' && !/^https?:\/\//.test(match)) return '';
}
return attr === 'style' ? '' : match;
}
);
return html;
}
最佳实践建议
-
使用专业库:推荐使用成熟的XSS过滤库
-
根据输出上下文处理:
// HTML内容 function filterHtml(content) { return DOMPurify.sanitize(content); } // HTML属性 function filterAttr(value) { return String(value) .replace(/"/g, '"') .replace(/'/g, '''); } // URL function filterUrl(url) { if (!/^https?:\/\//i.test(url)) return '#'; return url; }
-
结合CSP:设置内容安全策略头提供额外保护
专业库使用示例
// 使用DOMPurify
const clean = DOMPurify.sanitize(dirtyHtml, {
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a'],
ALLOWED_ATTR: ['href', 'title'],
FORBID_ATTR: ['style', 'onclick']
});
// 使用xss库
const filtered = filterXSS(originalHtml, {
whiteList: {
a: ['href', 'title']
},
onIgnoreTagAttr: function(tag, name, value) {
if (name === 'style') return '';
}
});
总结:在生产环境中建议使用专业的XSS过滤库,并根据不同的输出上下文实施针对性的过滤策略。