10个你可能不知道的超实用高级JavaScript技巧
提升你的编码技巧,利用这些冷门的JavaScript小技巧。
JavaScript 是一种多功能的语言,拥有许多隐藏的功能,可以让您的开发过程更高效,代码更整洁。这里有 10 个你可能不知道的高级 JavaScript 技巧,它们可以显著提升您的编程技能。
1. 解构别名解构允许你从数组或对象中拆包值和属性到不同的变量中。别名让你在解构过程中可以重命名这些变量,这在处理如API等外部来源的数据时特别有用。
场景: 当你从API获取数据的时候,你想给属性赋予更有意义的名字,以提高代码的可读性和维护性。
const apiResponse = { 名字: 'John', 年龄: 30, address: { 城市: 'New York', 邮编: '10001' } };
const { 名字: firstName, 年龄: age, address: { 城市: hometown, 邮编: postalCode } } = apiResponse;
console.log(firstName); // 约翰
console.log(age); // 30
console.log(hometown); // 纽约
console.log(postalCode); // 10001
为什么使用它: 它有助于使变量名更加自我解释和直观,从而提高代码的可读性和可维护性。通过使用别名功能,您可以避免名称冲突问题,并提高代码的清晰度,使其更易于处理复杂的数据结构。
2. 柯里化 (Currying)将一个多参数函数分解成一系列单参数函数的过程称为柯里化(Currying)。这种方法能让你创建更灵活且易于重用的函数,这种方法在函数式编程中特别有用。
用例: 创建一个可以重复使用和定制的函数来应用折扣。无需为不同的折扣百分比分别编写单独的函数,你可以使用一个函数式编程中的柯里化函数。
const 应用折扣 = (折扣) => (价格) => 价格 - (价格 * 折扣 / 100);
const 十折 = 应用折扣(10);
const 二十折 = 应用折扣(20);
console.log(十折(100)); // 90
控制台打印十折后的价格(100); // 90
console.log(二十折(100)); // 80
控制台打印二十折后的价格(100); // 80
const 应用税率 = (税率) => (价格) => 价格 + (价格 * 税率 / 100);
const 应用百分之十税率 = 应用税率(10);
console.log(应用百分之十税率(100)); // 110
控制台打印包含10%税后的价格(100); // 110
console.log(应用百分之十税率(二十折(100))); // 88
控制台打印包含10%税后且打了20折的价格; // 88
为什么要使用它: 它允许你在函数中预设参数,从而使代码更加模块化和可组合。这可以大大简化高度可重用实用函数的创建,使你的代码库更整洁且易于维护。柯里化函数在函数需要部分应用或以不同配置重用时特别有用。
3. 去抖动 和 限流防抖和节流技术是控制函数执行频率的方法。它们特别适用于优化事件处理函数,以避免过多的函数调用,这些调用可能会影响性能。
去抖
防抖确保在一个函数被再次调用之前,必须经过一定时间间隔。这在很多情况下非常有用,比如搜索框输入,希望在用户停止输入后再发起API调用。
案例: 优化搜索框的输入以减少API调用次数。这可以避免服务器过载,从而提升用户体验,只在用户输入完成后触发搜索。
function debounce(func, delay) {
let timeoutId;
return function(...args) {
清除超时ID;
timeoutId = setTimeout(() => func.apply(this, args), delay);
};
}
const search = debounce((query) => {
console.log(`正在搜索 ${query}`);
// 假设这里有一个API调用
}, 300);
获取元素 'searchInput' 并添加监听事件(input, (event) => {
搜索(event.target.value);
});
为什么使用它: 减少不必要的函数调用,确保函数在用户停止触发操作后才被调用,从而提升性能和用户体验。这对需要网络请求或进行大量计算的操作尤其有用。
限流
节流确保,函数在一个指定的时间段内最多只被调用一次。(这里指限制函数调用的频率)这在像滚动事件这样的场景中很有用,因为你希望限制函数调用的次数。
使用案例: 优化滚动事件处理来提高性能。这可以防止浏览器因过多事件调用而变得过载,确保更流畅和更快响应的交互体验。
function throttle(func, interval) {
let lastCall = 0;
return function(...args) {
const now = Date.now();
if (now - lastCall >= interval) {
lastCall = now;
func.apply(this, args);
}
};
}
const handleScroll = throttle(() => {
console.log('Scrolled');
// 假设这里进行了一些复杂的计算或DOM更新
}, 300);
// 这个函数用于限制函数调用的频率
window.addEventListener('scroll', handleScroll);
为什么要使用它: 防止性能问题,确保函数在规定的间隔内被调用,减少浏览器负担,从而提供更好的用户体验。节流特别有用,适用于可以频繁触发的事件监听器,例如滚动或调整窗口大小事件。
4 缓存化 (Memoization)Memoization 是一种优化技术,通过缓存计算结果来提高性能。当再次遇到相同输入时返回缓存的结果。这可以显著提高那些计算量大的函数的性能,尤其是那些经常用相同参数调用的函数。
应用场景: 提高像斐波那契数列计算这样的递归函数的效率。如果没有使用备忘录功能,每次调用该函数时都会重复计算相同的值,造成冗余,导致时间复杂度呈指数级上升。
const memoize = (fn) => {
const cache = {};
return (...args) => {
const key = JSON.stringify(args);
if (!cache[key]) {
cache[key] = fn(...args);
}
return cache[key];
};
};
// 缓存化函数,用于存储已计算的结果以提高性能
const fib = memoize((n) => {
if (n <= 1) return n;
return fib(n - 1) + fib(n - 2);
});
console.log(fib(40)); // 102334155
为什么使用它: 避免冗余计算,显著提高重复输入功能的性能。Memoization 可将重复的低效计算转换成可管理的线性时间操作,使其成为优化性能密集型任务的关键技术。
5 代理代理对象
允许你为另一个对象创建一个代理,使你能够拦截并重定义如属性查找、赋值、枚举和函数调用等基本操作。这提供了一种强大的方式,可以在对象上添加自定义行为。
使用案例: 对对象属性的访问和赋值进行验证和日志记录。例如,你可以强制执行类型限制并记录访问尝试,从而更好地控制和调试。
const user = {
name: 'John',
age: 30
};
const handler = {
get: (target, prop) => {
console.log(`打印 ${prop}`);
return target[prop];
},
set: (target, prop, value) => {
if (prop === 'age' && typeof value !== 'number') {
throw new TypeError('年龄必须是一个数字');
}
console.log(`尝试设置 ${prop} 为 ${value}`);
target[prop] = value;
return true;
}
};
const proxyUser = new Proxy(user, handler); // 创建一个新的代理对象proxyUser
console.log(proxyUser.name); // 打印 name, John
proxyUser.age = 35; // 尝试设置 age 为 35
// proxyUser.age = '35'; // 会抛出 TypeError
为什么使用它: 允许为对象操作定义自定义行为,例如验证、日志记录等,从而增强对对象操作的控制能力。代理也可以用来实现复杂的逻辑,比如访问权限控制和数据绑定功能。这使得它们成为管理并扩展对象行为的强大工具。
6. 生成器(Generators)生成器是一种可以暂停并继续执行的函数,它们在每次重新进入时可以保持其上下文和变量绑定的状态。生成器在实现迭代器和以同步的方式处理异步任务时十分有用。
用例: 实现自定义对象的遍历。生成器可以简单地定义自定义的迭代行为,从而使遍历复杂的数据结构变得更简单。
function* objectEntries(obj) {
for (let key of Object.keys(obj)) {
yield [key, obj[key]];
}
}
const user = { name: 'John', age: 30, city: 'New York' };
for (let [key, value] of objectEntries(user)) {
console.log(`${key}: ${value}`);
}
// 名称: John
// 年龄: 30
// 城市: 纽约
为什么要用它: 提供了一种强大的工具,用于实现自定义迭代器并简化异步工作流。生成器使得处理复杂的迭代逻辑和异步过程更加容易,从而使代码更易读和维护。它们还可以用于使用如 co
这样的库,以更简单和线性的方式处理异步操作。
场景: 改进调试复杂对象的日志记录。比如 console.table
、console.group
和 console.time
这样的控制台方法可以提供更结构化和详尽的信息,有助于调试。
// 基本日志记录
console.log('简单的日志记录');
console.error('这是一个错误');
console.warn('这是一个警告信息');
// 表格数据日志记录
const users = [
{ name: 'John', age: 30, city: 'New York' },
{ name: 'Jane', age: 25, city: 'San Francisco' },
];
console.table(users); // 表格数据
// 分组日志
console.group('用户信息');
console.log('用户1: John');
console.log('用户2: Jane');
console.groupEnd();
// 计量代码执行时间
console.time('计时器');
for (let i = 0; i < 1000000; i++) {
// 一些耗时的计算
}
console.timeEnd('计时器'); // 计时结束
为什么要用它: 让调试信息更清晰和有组织,使问题诊断和解决变得更简单。正确使用控制台方法能通过提供清晰、有条理和详细的日志,显著提高你的调试效率。
8. 结构化克隆方法structuredClone
使用 structuredClone
进行结构化克隆
使用新的 structuredClone
方法可以深度克隆对象。这意味着结构化克隆会创建对象的深度副本,确保每个嵌套的对象都会被复制。此方法避免了 JSON.parse(JSON.stringify(obj))
的局限性,后者无法处理某些数据类型,如函数、undefined
和循环引用等。
应用场景: 为复杂对象创建深拷贝。当你需要复制对象而不改变原始数据时,这很有用。
const obj = {
a: 1,
b: { c: 2 },
date: new Date(),
arr: [1, 2, 3],
nestedArr: [{ d: 4 }]
};
const clonedObj = structuredClone(obj);
console.log(clonedObj);
// { a: 1, b: { c: 2 }, date: 2023-06-08T00:00:00.000Z, arr: [1, 2, 3], nestedArr: [{ d: 4 }] } // 输出克隆的对象
console.log(clonedObj === obj); // false // 检查克隆的对象是否等于原始对象
console.log(clonedObj.b === obj.b); // false // 检查克隆对象中的子对象是否等于原始对象中的子对象
console.log(clonedObj.date === obj.date); // false // 检查克隆对象中的日期是否等于原始对象中的日期
console.log(clonedObj.arr === obj.arr); // false // 检查克隆对象中的数组是否等于原始对象中的数组
console.log(clonedObj.nestedArr[0] === obj.nestedArr[0]); // false // 检查克隆对象中的嵌套数组的第一个元素是否等于原始对象中的嵌套数组的第一个元素
为什么使用它: 提供了一种内置且高效的深度克隆对象的方法,避免了手动实现深度复制时可能遇到的陷阱和复杂性。相比之下,这种方法比像 JSON.parse(JSON.stringify(obj))
这样的替代方案更加可靠,并且在处理复杂的数据结构方面也更加出色。
自执行函数,也称为立即执行函数表达式(IIFE),一创建就会自动执行。它们将代码封装以避免污染全局变量,这对于保持代码整洁和模块化至关重要。
用例: 封装代码以避免污染全局作用域,这种方法在较旧的JavaScript环境中特别有用,特别是在那些没有块级作用域(如let和const)的地方。此外,在需要立即执行初始化相关的逻辑时也非常有用。
(function() {
const privateVar = '这是私有变量';
console.log('立即调用的函数运行');
// 初始化代码可以放在这里
})();
// 私有变量不能从外部访问
// console.log(privateVar); // ReferenceError: privateVar 未被定义
为什么使用它: 可以通过避免全局变量并执行不留下痕迹的初始化代码来保持代码的整洁。这种方法可以在更大的代码库中防止冲突,更好地封装功能,提高代码的可维护性并避免副作用。
10. 带标签的模板文字带标签的模板字面量允许你自定义模板字面量的处理方式,非常适合用于国际化、净化 HTML 内容或生成动态 SQL 语句。
用例: 在HTML模板中净化用户输入以防止跨站脚本攻击。此方法确保用户生成的内容可以安全地插入到DOM中,而不执行任何恶意代码。
function sanitize(strings, ...values) {
return strings.reduce((result, string, i) => {
let value = values[i - 1];
if (typeof value === 'string') {
value = value.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
return result + value + string;
});
}
const userInput = '<script>alert("xss")</script>';
const message = sanitize`用户输入: ${userInput}`;
console.log(message); // 用户输入: <script>alert("xss")</script>
// `sanitize` 函数用于清理用户输入,防止XSS攻击。
使用它的原因: 提供了一种强大的方法来控制和自定义模板字面量的结果,从而实现更安全和灵活的模板。例如,可以通过这种方法确保安全性,格式化字符串并生成动态内容,增强代码的稳定性和灵活性。
最后JavaScript 是一个功能强大的语言,可以帮助你编写更干净、更高效的代码。通过将这些高级技巧融入你的编程实践中,你可以提高生产效率并增强代码的可读性。从带别名的解构到柯里化函数、防抖、节流等等,这些技巧可以使你的代码更干净、更高效。编码愉快!
共同学习,写下你的评论
评论加载中...
作者其他优质文章