你不知道的 10 个高级 JavaScript 技巧

你不知道的 10 个高级 JavaScript 技巧

JavaScript 是一种多功能语言,具有许多隐藏功能,可以提高开发过程的效率和代码的简洁性。这里有 10 个你可能不知道的高级 JavaScript 技巧,但它们可以显着提高你的编码技能。

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. 柯里化(Currying)

柯里化是将接受多个参数的函数转换为一系列函数的过程,每个函数都接受一个参数。此技术允许您创建更灵活和可重用的函数,这在函数式编程中特别有用。

用例:创建可重用和可配置的功能以应用折扣。您可以创建一个柯里化函数,而不是为不同的折扣百分比编写单独的函数。

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. 防抖(Debouncing)和节流(throttling)

防抖和节流是控制函数执行频率的技术。它们对于优化事件处理程序以防止可能降低性能的过多函数调用特别有用。

防抖:

防抖确保函数在自上次调用后经过一定时间后才会再次调用。这对于搜索输入字段等场景非常有用,在这些场景中,您希望等到用户停止键入后再进行 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}`);
  // Imagine an API call here
}, 300);

document.getElementById('searchInput').addEventListener('input', (event) => {
  search(event.target.value);
});

为什么使用它:减少不必要的函数调用次数,通过确保仅在用户停止执行触发操作后调用函数来提高性能和用户体验。这对于涉及网络请求或大量计算的操作特别有用。

节流:

节流可确保在指定时间段内最多调用一次函数。这对于要限制函数调用频率的滚动事件等场景非常有用。

用例:优化滚动事件处理以提高性能。这可以防止浏览器被过多的事件调用淹没,从而确保更流畅、响应更灵敏的交互。

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}`);
  // Imagine an API call here
}, 300);

document.getElementById('searchInput').addEventListener('input', (event) => {
  search(event.target.value);
});

为什么使用它:通过确保以受控的时间间隔调用函数来防止性能问题,从而减少浏览器上的负载并提供更好的用户体验。限制对于可以频繁触发的事件侦听器特别有用,例如滚动或调整大小事件。

4. 记忆化

记忆化是一种优化技术,涉及缓存昂贵的函数调用的结果,并在再次出现相同的输入时返回缓存的结果。这可以显著提高计算量大的函数的性能,尤其是那些经常使用相同参数调用的函数。

用例:提高递归函数(如 Fibonacci 计算)的性能。如果没有记忆化,每次调用 Fibonacci 函数都会重复多次计算相同的值,从而导致指数级时间复杂度。

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.tableconsole.groupconsole.time

// Basic logging
console.log('Simple log');
console.error('This is an error');
console.warn('This is a warning');

// Logging tabular data
const users = [
  { name: 'John', age: 30, city: 'New York' },
  { name: 'Jane', age: 25, city: 'San Francisco' },
];
console.table(users);

// Grouping logs
console.group('User Details');
console.log('User 1: John');
console.log('User 2: Jane');
console.groupEnd();

// Timing code execution
console.time('Timer');
for (let i = 0; i < 1000000; i++) {
  // Some heavy computation
}
console.timeEnd('Timer');

为什么使用它:增强调试信息的可见性和组织性,从而更轻松地诊断和修复问题。正确使用控制台方法可以通过提供清晰、有序和详细的日志来显著提高调试过程的效率。

8. 结构化克隆structuredClone

使用新的 .与传统的浅层复制不同,结构化克隆会创建对象的深层副本,从而确保嵌套对象也被复制。此方法避免了 的限制,它无法处理某些数据类型,如函数、 和循环引用。structuredCloneJSON.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))

9. 自调用函数

自调用函数,也称为立即调用的函数表达式 (IIFE),在创建后自动执行。它们可用于封装代码以避免污染全局范围,这对于维护干净和模块化的代码至关重要。

用例:封装代码以避免污染全局范围。该技术在块范围(let 和 const)不可用的旧 JavaScript 环境中,或者在初始化逻辑需要立即执行的情况下特别有用。

(function() {
  const privateVar = 'This is private';
  console.log('Self-invoking function runs immediately');
  // Initialization code here
})();

// Private variables are not accessible from outside
// 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>

为什么使用它:提供强大的机制来控制和自定义模板文字的输出,从而实现更安全、更灵活的模板创建。标记的模板文本可用于强制实施安全性、格式化字符串和生成动态内容,从而增强代码的稳健性和多功能性。

结论

JavaScript 是一种功能丰富的语言,可以帮助您编写更简洁、更高效的代码。通过将这些高级技巧整合到您的编码实践中,您可以提高工作效率并增强代码的可读性。从使用别名解构到局部套用、去抖动、限制等,这些技巧可以使您的代码更简洁、更高效。

© 版权声明
THE END
喜欢就支持一下吧
点赞10 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容