1. 带别名的解构
解构允许您从数组中提取值或将对象的属性解包到不同的变量中。别名功能使您能够在这一过程中重命名变量,这在处理来自外部源(如API)的数据时特别有用。
例子:从API获取数据时,如果您想为属性赋予更有意义的名称,以提高代码的可读性和可维护性。
const apiResponse = { first_name: 'John', user_age: 30, address: { city: 'New York', zip: '10001' } };
const { first_name: firstName, user_age: age, address: { city: hometown, zip: postalCode } } = apiResponse;
console.log(firstName); // John
console.log(age); // 30
console.log(hometown); // New York
console.log(postalCode); // 10001
它有助于使变量名称更具自解释性和直观性,从而提高代码的可读性和可维护性。通过使用别名,您可以避免名称冲突,并提高代码的清晰度,使其更易于处理复杂的数据结构。
2. 柯里化
柯里化是将一个接受多个参数的函数转换为一系列每个都接受单个参数的函数的过程。此技术允许您创建更灵活和可重用的函数,这在函数式编程中特别有用。
例子:为应用折扣创建可重用和可配置的函数。无需为不同的折扣百分比编写单独的函数,您可以创建一个单一的柯里化函数。
const applyDiscount = (discount) => (price) => price - (price * discount / 100);
const tenPercentOff = applyDiscount(10);
const twentyPercentOff = applyDiscount(20);
console.log(tenPercentOff(100)); // 90
console.log(twentyPercentOff(100)); // 80
const applyTax = (taxRate) => (price) => price + (price * taxRate / 100);
const applyTenPercentTax = applyTax(10);
console.log(applyTenPercentTax(100)); // 110
console.log(applyTenPercentTax(twentyPercentOff(100))); // 88
它使您能够在函数中预设参数,从而实现更模块化和可组合的代码。这可以极大地简化高度可重用实用函数的创建,使您的代码库更整洁且更易于维护。柯里化在需要部分应用函数或使用不同配置重用函数的场景中特别有用。
3. 防抖和节流
防抖和节流是控制函数执行频率的技术。它们对于优化事件处理程序以防止可能降低性能的过度函数调用特别有用。
防抖
防抖确保在上次调用后的特定时间段内不再再次调用函数。这在诸如搜索输入字段之类的场景中很有用,您希望等到用户停止输入后再进行API调用。
例子:优化搜索输入字段以减少API调用的数量。这可以防止服务器过载,并通过仅在用户完成输入后启动搜索来改善用户体验。
function debounce(func, delay) {
let timeoutId;
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => func.apply(this, args), delay);
};
}
const search = debounce((query) => {
console.log(`Searching for ${query}`);
// 想象这里有一个API调用
}, 300);
document.getElementById('searchInput').addEventListener('input', (event) => {
search(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. 记忆化
记忆化是一种优化技术,涉及缓存昂贵函数调用的结果,并在再次出现相同输入时返回缓存的结果。这可以显著提高具有繁重计算的函数的性能,特别是那些频繁使用相同参数调用的函数。
例子:提高诸如斐波那契计算之类的递归函数的性能。如果没有记忆化,对斐波那契函数的每次调用都会多次冗余地计算相同的值,导致指数级的时间复杂度。
const memoize = (fn) => {
const cache = {};
return (...args) => {
const key = JSON.stringify(args);
if (!cache[key]) {
cache[key] = fn(...args);
}
return cache[key];
};
};
const fibonacci = memoize((n) => {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
});
console.log(fibonacci(40)); // 102334155
避免冗余计算,显著提高具有重复输入的函数的性能。记忆化可以将低效、重复的计算转换为可管理的线性时间操作,使其成为优化性能密集型任务的基本技术。
5. 代理
Proxy
对象允许您为另一个对象创建代理,使您能够拦截和重新定义基本操作,例如属性查找、赋值、枚举、函数调用等。这为向对象添加自定义行为提供了一种强大的方式。
例子:在对象属性访问和赋值时进行验证和日志记录。例如,您可以强制类型约束并记录访问尝试,提供更好的控制和调试功能。
const user = {
name: 'John',
age: 30
};
const handler = {
get: (target, prop) => {
console.log(`Getting ${prop}`);
return target[prop];
},
set: (target, prop, value) => {
if (prop === 'age' && typeof value !== 'number') {
throw new TypeError('Age must be a number');
}
console.log(`Setting ${prop} to ${value}`);
target[prop] = value;
return true;
}
};
const proxyUser = new Proxy(user, handler);
console.log(proxyUser.name); // Getting name, John
proxyUser.age = 35; // Setting age to 35
// proxyUser.age = '35'; // Throws TypeError
允许为对象操作自定义行为,例如验证、日志记录等,增强对对象操作的控制。代理还可用于实现诸如访问控制和数据绑定之类的复杂逻辑。这使其成为管理和扩展对象行为的多功能工具。
6. 生成器
生成器是可以退出然后再重新进入的函数,在重新进入时能保持其上下文和变量绑定。它们对于实现迭代器和以类似同步的方式处理异步任务很有用。
例子:为自定义对象遍历实现迭代器。生成器为定义自定义迭代行为提供了一种简单的方式,使遍历复杂数据结构变得更容易。
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}`);
}
// name: John
// age: 30
// city: New York
为实现自定义迭代器和简化异步工作流提供了强大的工具。生成器使处理复杂的迭代逻辑和异步进程变得更容易,从而导致更具可读性和可维护性的代码。它们还可以用于诸如使用co
之类的库以更直接、线性的方式管理异步操作等任务。
7、善用控制台
例子:改进复杂对象的调试日志记录。诸如console.table
、console.group
和console.time
等控制台方法可以提供更结构化和信息丰富的调试信息。
// 基本日志记录
console.log('Simple log');
console.error('This is an error');
console.warn('This is a warning');
// 记录表格数据
const users = [
{ name: 'John', age: 30, city: 'New York' },
{ name: 'Jane', age: 25, city: 'San Francisco' },
];
console.table(users);
// 分组日志
console.group('User Details');
console.log('User 1: John');
console.log('User 2: Jane');
console.groupEnd();
// 计时代码执行
console.time('Timer');
for (let i = 0; i < 1000000; i++) {
// 一些繁重的计算
}
console.timeEnd('Timer');
增强了调试信息的可见性和组织性,使诊断和修复问题变得更容易。正确使用控制台方法可以通过提供清晰、有组织和详细的日志显著提高调试过程的效率。
8. 使用structuredClone
进行结构化克隆
使用新的structuredClone
进行深度克隆对象。与传统的浅拷贝不同,结构化克隆创建对象的深度副本,确保嵌套对象也被复制。此方法避免了JSON.parse(JSON.stringify(obj))
的限制,后者无法处理诸如函数、未定义和循环引用等某些数据类型。
例子:创建复杂对象的深度副本。当您需要复制对象以进行不应更改原始数据的操作时很有用。
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))
之类的替代方法更可靠,并且能更好地处理复杂的数据结构。
9. 自调用函数
自调用函数,也称为立即调用函数表达式(IIFE),在创建后会自动执行。它们对于封装代码以避免污染全局作用域很有用,这对于维护干净和模块化的代码至关重要。
例子:封装代码以避免污染全局作用域。此技术在较旧的JavaScript环境(其中块作用域(let
和const
)不可用)或需要立即执行初始化逻辑的场景中特别有用。
(function() {
const privateVar = 'This is private';
console.log('Self-invoking function runs immediately');
// 初始化代码在此处
})();
// 私有变量无法从外部访问
// console.log(privateVar); // ReferenceError: privateVar is not defined
通过避免全局变量和在全局作用域中不留下痕迹地执行初始化代码来帮助维护干净的代码。这种方法可以防止在较大的代码库中发生冲突,并确保更好地封装功能,提高代码的可维护性并避免副作用。
10. 带标签的模板字面量
带标签的模板字面量允许您自定义模板字面量的处理方式。它们对于创建专门的模板很有用,例如用于国际化、清理HTML或生成动态SQL查询。
例子:在HTML模板中清理用户输入以防止XSS攻击。此技术确保用户生成的内容安全地插入到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`User input: ${userInput}`;
console.log(message); // User input: <script>alert("xss")</script>
提供了一种强大的机制来控制和自定义模板字面量的输出,实现更安全和更灵活的模板创建。带标签的模板字面量可用于加强安全性、格式化字符串和生成动态内容,增强代码的健壮性和通用性。
总结:
参考文献:https://medium.com/@bjprajapati381/10-advanced-javascript-tricks-you-dont-know-f1929e40703d